예제 #1
0
    def __init__(self, parent: AppWidget):
        super().__init__(parent, "Time")

        d = TimeWidget(self,
                       Config.DateTime.date_format(),
                       h_alignment=TextWidget.HAlignment.CENTER)
        d.rectangle = Rectangle(AnchoredPoint(0, 0, Anchor.TOP_LEFT),
                                self.size)
        d.fit_font_size()
        d.set_height(d.preferred_size.height, VAnchor.TOP)

        t = TimeWidget(self, Config.DateTime.time_format())
        t.rectangle = Rectangle(
            d.position(Anchor.BOTTOM_LEFT).anchored(Anchor.TOP_LEFT) +
            Point(0, 2), self.size)
        t.fit_font_size()
        t.set_height(t.preferred_size.height, VAnchor.TOP)

        h = self.height - t.bottom
        s = SplitSecondSpinner(self)
        s.rectangle = Rectangle(
            t.position(Anchor.BOTTOM_RIGHT).anchored(Anchor.TOP_RIGHT),
            Size(h, h))

        self.uptime = App.uptime()
        self.uptime_widget = TextWidget(self, self._uptime(), Font(size=10))
        self.uptime_widget.rectangle = Rectangle(
            self.position(Anchor.BOTTOM_LEFT),
            Size(s.left, self.uptime_widget.preferred_size.height))
        self.app.scheduler.schedule_synchronous(timedelta(minutes=1),
                                                self._update_uptime)
        self._update_uptime()
예제 #2
0
def draw_rounded_rectangle(ctx: Context, rectangle: Rectangle, radius: float):
    """
    Draw rectangles with rounded (circular arc) corners.
    :param ctx: Cairo render context.
    :param rectangle: Extent of the rectangle.
    :param radius: Radius of the corners.
    """
    from math import pi
    a, c = rectangle.position(Anchor.TOP_LEFT)
    b, d = rectangle.position(Anchor.BOTTOM_RIGHT)
    ctx.arc(a + radius, c + radius, radius, 2 * (pi / 2), 3 * (pi / 2))
    ctx.arc(b - radius, c + radius, radius, 3 * (pi / 2), 4 * (pi / 2))
    ctx.arc(b - radius, d - radius, radius, 0 * (pi / 2), 1 * (pi / 2))  # ;o)
    ctx.arc(a + radius, d - radius, radius, 1 * (pi / 2), 2 * (pi / 2))
    ctx.close_path()
예제 #3
0
 def do_layout(self):
     for child in self.children:
         if isinstance(child, TextWidget):
             child.set_size(Size(self.width, child.height), Anchor.TOP_LEFT)
     self._icon_widget.rectangle = Rectangle(
         self.size.position(Anchor.CENTER_RIGHT) + Point(5, 0),
         self.size / 1.5)
예제 #4
0
    def paint_foreground(self, ctx: Context):
        ctx.set_source_rgba(*Color.GRAY40)
        rect = Rectangle(ZERO_TOP_LEFT, self.size)
        center = rect.position(Anchor.CENTER_CENTER)
        r = min(*self.size) / 2
        ctx.move_to(center.x, center.y)
        φ = 2 * math.pi * datetime.now().microsecond / 1000000
        ctx.arc(*center, r, -math.pi / 2, φ - math.pi / 2)
        ctx.line_to(center.x, center.y)
        ctx.fill()

        ctx.move_to(center.x, center.y)
        ctx.line_to(center.x + r * math.sin(φ), center.y - r * math.cos(φ))
        ctx.set_line_width(2)
        ctx.set_source_rgba(*Color.WHITE)
        ctx.stroke()
예제 #5
0
    def __init__(self,
                 parent: ContainerWidget,
                 weather_period: Optional[WeatherPeriod],
                 download_manager: DownloadManager,
                 font: Font = Font(size=9, bold=True)):
        super().__init__(parent)
        self._download_manager = download_manager
        self.font = font

        self._icon_widget = ImageWidget(self)
        self._icon_widget.rectangle = Rectangle(
            self.size.position(Anchor.CENTER_RIGHT), self.size / 1.5)

        self._from_to_widget: TextWidget = TextWidget(self, '00:00-00:00',
                                                      self.font)
        self._from_to_widget.rectangle = Rectangle(
            AnchoredPoint(0, 1, Anchor.TOP_LEFT),
            self._from_to_widget.preferred_size)
        self._from_to_widget.h_alignment = TextWidget.HAlignment.CENTER
        self._from_to_widget.background = None

        self._temp_widget = TextWidget(
            self, '-11.3°C',
            dataclasses.replace(self.font, size=self.font.size * 1.5))
        self._temp_widget.rectangle = Rectangle(
            self._from_to_widget.position(Anchor.BOTTOM_LEFT).anchored(
                Anchor.TOP_LEFT) + Point(0, 4),
            self._temp_widget.preferred_size)
        self._temp_widget.foreground = Color.RED
        self._temp_widget.background = None

        self._cloudiness_widget = TextWidget(self, '0/0', self.font)
        self._cloudiness_widget.rectangle = Rectangle(
            self._temp_widget.position(Anchor.BOTTOM_LEFT).anchored(
                Anchor.TOP_LEFT) + Point(0, 4),
            self._cloudiness_widget.preferred_size)
        self._cloudiness_widget.background = None

        self._rain_widget = TextWidget(self, '22.3mm 100%', self.font)
        self._rain_widget.rectangle = Rectangle(
            self._cloudiness_widget.position(Anchor.BOTTOM_LEFT).anchored(
                Anchor.TOP_LEFT) + Point(0, 4),
            self._rain_widget.preferred_size)
        self._rain_widget.background = None

        self.weather_period = weather_period
예제 #6
0
    def __init__(self,
                 parent: ContainerWidget,
                 weather_periods: Optional[List[WeatherPeriod]],
                 download_manager: DownloadManager,
                 font: Font = Font(size=12)):
        super().__init__(parent)
        self._weather_periods = weather_periods
        self._font = font
        for i in range(5):
            w = WeatherWidget(self, None, download_manager)
            w.rectangle = Rectangle(
                AnchoredPoint(i * (w.preferred_size.width + 3), 0,
                              Anchor.TOP_LEFT), w.preferred_size)

            l: Line = Line(self, Line.Orientation.VERTICAL)
            l.foreground = Color.GRAY67
            l.rectangle = Rectangle(
                w.position(Anchor.TOP_RIGHT).anchored(Anchor.TOP_LEFT),
                Size(l.preferred_size().width, w.height))
        self._update_children()
예제 #7
0
 def build_menu(self):
     self.children.clear()
     y = 0
     for i in range(len(self.menu)):
         entry = self.menu[i]
         a = TextWidget(self, entry.text, self.font)
         a.rectangle = Rectangle(AnchoredPoint(0, y, Anchor.TOP_LEFT),
                                 a.preferred_size)
         if i == self.current_entry:
             a.foreground = self.background
             a.background = self.foreground
         entry.widget = a
예제 #8
0
    def __init__(self, parent: AppWidget):
        super().__init__(parent, "Main")

        self.lv2_3 = Line(self, Line.Orientation.VERTICAL)
        self.lv2_3.rectangle = Rectangle(AnchoredPoint(self.width / 3 * 2, 0, Anchor.TOP_LEFT),
                                         Size(self.lv2_3.preferred_size().width, self.height))

        self.date = TimeWidget(self, Config.DateTime.date_format(), h_alignment=TextWidget.HAlignment.CENTER)
        self.date.rectangle = Rectangle(self.lv2_3.position(Anchor.TOP_RIGHT).anchored(Anchor.TOP_LEFT),
                                        self.position(Anchor.BOTTOM_RIGHT))
        self.date.font = dataclasses.replace(self.date.font, bold=True)
        self.date.fit_font_size()
        self.date.set_height(self.date.preferred_size.height, VAnchor.TOP)
        self.date.foreground = Color.GRAY75

        self.time = TimeWidget(self, Config.DateTime.time_format())
        self.time.rectangle = Rectangle(self.date.position(Anchor.BOTTOM_LEFT).anchored(Anchor.TOP_LEFT) + Point(0, 2),
                                        self.position(Anchor.BOTTOM_RIGHT))
        self.time.fit_font_size()
        self.time.set_height(self.time.preferred_size.height, VAnchor.TOP)
        self.time.foreground = self.date.foreground

        self.lh1 = Line(self, Line.Orientation.HORIZONTAL)
        self.lh1.rectangle = Rectangle(self.time.position(Anchor.BOTTOM_LEFT).anchored(Anchor.TOP_LEFT),
                                       Size(self.width - self.lv2_3.left, self.lh1.preferred_size().height))

        self.wetter_com = WetterCom(Config.Weather.city_code(), Global.download_manager)
        self.weather_widgets = WeatherWidgets(self, None, Global.download_manager)
        self.weather_widgets.rectangle = Rectangle(self.position(Anchor.BOTTOM_LEFT),
                                                   self.weather_widgets.preferred_size)
        self.load_weather()
        self.app.scheduler.schedule_synchronous(timedelta(minutes=10), self.load_weather)

        temp_font = Font(size=11, bold=True)
        self.out_temp = TextWidget(self, 'Out: -00.0° - -00.0°', temp_font)
        self.out_temp.rectangle = Rectangle(self.weather_widgets.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT)
                                            + Point(0, -1),
                                            self.out_temp.preferred_size + Size(0, 2))
        self.out_temp.foreground = Color.GRAY80
        self.out_temp.escape = False

        self.in_temp = TextWidget(self, 'In: -00.0° - -00.0°', temp_font)
        self.in_temp.rectangle = Rectangle(AnchoredPoint(self.width, self.out_temp.top, Anchor.TOP_RIGHT),
                                           self.in_temp.preferred_size)
        self.in_temp.foreground = Color.GRAY80
        self.in_temp.escape = False
        self.in_temp.h_alignment = TextWidget.HAlignment.RIGHT

        self.balcony_temp = TextWidget(self, 'Blk: -00.0°', temp_font)
        self.balcony_temp.rectangle = Rectangle(AnchoredPoint((self.out_temp.right + self.in_temp.left) / 2,
                                                              self.out_temp.top, Anchor.TOP_CENTER),
                                                self.balcony_temp.preferred_size)
        self.balcony_temp.foreground = Color.GRAY80
        self.balcony_temp.escape = False
        self.balcony_temp.h_alignment = TextWidget.HAlignment.CENTER

        def get_temp_values(_): Global.download_manager.get(Config.Weather.temp_values_url(),
                                                            self.load_temp_values,
                                                            timedelta(seconds=29))

        get_temp_values(None)
        self.app.scheduler.schedule_synchronous(timedelta(minutes=1), get_temp_values)

        self.lh3 = Line(self, Line.Orientation.HORIZONTAL)
        self.lh3.rectangle = Rectangle(self.out_temp.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT)
                                       + Point(0, -1),
                                       Size(self.width, self.lh3.preferred_size().height))

        self.track_title = MediaPlayerTrackTitleWidget(self, Global.media_player, Font(size=14))
        self.track_title.rectangle = Rectangle(self.lh3.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT),
                                               Size(self.width, self.track_title.font.font_extents().height))

        track_position_font = Font(size=11, bold=True)
        self.track_position = MediaPlayerTrackPositionWidget(self, Global.media_player, track_position_font)
        self.track_position.rectangle = Rectangle(
            self.track_title.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT),
            Size(self.width / 3, self.track_position.font.font_extents().height))

        self.track_duration = MediaPlayerTrackDurationWidget(self, Global.media_player, track_position_font)
        self.track_duration.rectangle = Rectangle(
            self.track_title.position(Anchor.TOP_CENTER).anchored(Anchor.BOTTOM_CENTER),
            Size(self.width / 3, self.track_duration.font.font_extents().height))
        self.track_duration.h_alignment = TextWidget.HAlignment.CENTER

        self.track_remaining = MediaPlayerTrackRemainingWidget(self, Global.media_player, track_position_font)
        self.track_remaining.rectangle = Rectangle(
            self.track_title.position(Anchor.TOP_RIGHT).anchored(Anchor.BOTTOM_RIGHT),
            Size(self.width / 3, self.track_remaining.font.font_extents().height))
        self.track_remaining.h_alignment = TextWidget.HAlignment.RIGHT

        self.lh2 = Line(self, Line.Orientation.HORIZONTAL)
        self.lh2.rectangle = Rectangle(
            self.track_position.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT) + Point(0, -1),
            Size(self.width, self.lh2.preferred_size().height))

        self.lv2_3.set_height(self.lh2.bottom, VAnchor.TOP)

        self.cpu_load_text = CpuLoadTextWidget(self, Font(size=12), h_alignment=TextWidget.HAlignment.CENTER)
        self.cpu_load_text.rectangle = Rectangle(self.lh2.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT)
                                                 + Point(0, -1), self.cpu_load_text.preferred_size)
        self.cpu_load_text.foreground = Color.GRAY90

        self.cpu_load_bar = CpuLoadBarWidget(self, BarWidget.Orientation.VERTICAL_UP, Color.GRAY75)
        self.cpu_load_bar.rectangle = Rectangle(
            self.cpu_load_text.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT),
            Size(self.cpu_load_text.width, self.cpu_load_text.top))

        self.mem_stats_bar = MemStatsBar(self, Font(size=12), Color.GRAY75)
        self.mem_stats_bar.rectangle = \
            Rectangle(AnchoredPoint(self.cpu_load_bar.right, 0, Anchor.TOP_LEFT) + Point(1, 0),
                      Size(self.lv2_3.left - self.cpu_load_bar.right - 2, self.cpu_load_bar.width))
        self.mem_stats_bar.foreground = Color.WHITE.with_value(alpha=0.9)

        self.disk_stats = DiskStats(self, Font(size=12))
        self.disk_stats.rectangle = Rectangle(self.lv2_3.position(Anchor.BOTTOM_LEFT).anchored(Anchor.BOTTOM_RIGHT)
                                              + Point(0, -3), self.cpu_load_text.position(Anchor.TOP_RIGHT))
        self.disk_stats.h_alignment = TextWidget.HAlignment.CENTER

        self.lhs = Line(self, Line.Orientation.HORIZONTAL)
        self.lhs.rectangle = Rectangle(self.disk_stats.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT)
                                       + Point(0, -1), Size(self.disk_stats.width, self.lhs.preferred_size().height))
        self.lhs.foreground = Color.GRAY50

        self.processes = ProcessList(self, 5, Font(size=12))
        self.processes.rectangle = Rectangle(self.mem_stats_bar.position(Anchor.BOTTOM_LEFT).anchored(Anchor.TOP_LEFT)
                                             + Point(2, 5),
                                             self.lhs.position(Anchor.TOP_RIGHT))

        self.fritz_box = FritzBox(self.app.scheduler, Config.FritzBox.address(), Config.FritzBox.password())
        self.fritz_box_connected = FritzBoxConnectedWidget(self, self.fritz_box, Font(size=12))
        self.fritz_box_connected.rectangle = Rectangle(
            self.lh1.position(Anchor.BOTTOM_LEFT).anchored(Anchor.TOP_LEFT) + Point(1, 1),
            Size(self.lh1.width, self.fritz_box_connected.preferred_size.height))

        self.fritz_box_speed = FritzBoxSpeedWidget(self, self.fritz_box, Font(size=12))
        self.fritz_box_speed.rectangle = Rectangle(
            self.fritz_box_connected.position(Anchor.BOTTOM_LEFT).anchored(Anchor.TOP_LEFT) + Point(0, 1),
            Size(self.fritz_box_connected.width, self.fritz_box_speed.preferred_size.height))

        self.fritz_box_hosts = FritzBoxHostsWidget(self, self.fritz_box, Font(size=12))
        self.fritz_box_hosts.rectangle = Rectangle(
            self.fritz_box_speed.position(Anchor.BOTTOM_LEFT).anchored(Anchor.TOP_LEFT) + Point(0, 1),
            Size(self.fritz_box_connected.width, self.fritz_box_hosts.preferred_size.height))

        self.fritz_box_ip6 = FritzBoxIp6Widget(self, self.fritz_box, Font(size=12))
        self.fritz_box_ip6.rectangle = Rectangle(
            self.lv2_3.position(Anchor.BOTTOM_RIGHT).anchored(Anchor.BOTTOM_LEFT) + Point(1, -3),
            Size(self.fritz_box_connected.width, self.fritz_box_ip6.preferred_size.height))
        self.fritz_box_ip6.fit_font_size()
        self.fritz_box_ip6.set_height(self.fritz_box_ip6.preferred_size.height, VAnchor.BOTTOM)

        self.fritz_box_ip4 = FritzBoxIp4Widget(self, self.fritz_box, Font(size=12))
        self.fritz_box_ip4.rectangle = Rectangle(
            self.fritz_box_ip6.position(Anchor.TOP_LEFT).anchored(Anchor.BOTTOM_LEFT) + Point(0, -1),
            Size(self.fritz_box_connected.width, self.fritz_box_ip4.preferred_size.height))

        self.fritz_box_traffic = FritzBoxTrafficWidget(self, self.fritz_box, Font(size=12))
        self.fritz_box_traffic.rectangle = Rectangle(
            self.fritz_box_hosts.position(Anchor.BOTTOM_LEFT).anchored(Anchor.TOP_LEFT) + Point(0, 1),
            self.fritz_box_ip4.position(Anchor.TOP_RIGHT) + Point(0, -1))
예제 #9
0
 def set_size(self, size: Size, anchor: Anchor):
     self.rectangle = Rectangle(self.rectangle.position(anchor), size)
예제 #10
0
 def set_position(self, point: AnchoredPoint):
     """
     Moves this widget while retaining it's size.
     """
     self.rectangle = Rectangle(point, self.size)
예제 #11
0
 def rectangle(self) -> Rectangle:
     return Rectangle(ZERO_TOP_LEFT, self.screen_size)
예제 #12
0
 def set_height(self, height: float, anchor: VAnchor):
     self.rectangle = Rectangle(self.position(anchor + HAnchor.LEFT),
                                Size(self.width, height))
     self.dirty = True
예제 #13
0
 def set_width(self, width: float, anchor: HAnchor):
     self.rectangle = Rectangle(self.position(VAnchor.TOP + anchor),
                                Size(width, self.width))
예제 #14
0
class Widget(ABC):
    """ Base class for all widgets. """
    __metaclass__ = ABCMeta
    _parent: ContainerWidget
    _rectangle: Rectangle = clear19.widgets.geometry.ZERO_RECT
    _dirty: bool = True
    _background: Optional[Color]
    _foreground: Color

    def __init__(self, parent: ContainerWidget):
        """
        :param parent: The container which holds this widget.
        """
        self._background = parent.background
        self._foreground = parent.foreground
        self._parent = parent
        if parent is not self:
            parent.children.append(self)

    @property
    def parent(self) -> ContainerWidget:
        return self._parent

    @property
    def dirty(self) -> bool:
        """
        :return: If true, this widget will be rendered soon.
        """
        return self._dirty

    @dirty.setter
    def dirty(self, dirty: bool):
        """ If set to True, the parent is also set to dirty. """
        if dirty != self._dirty:
            self._dirty = dirty
            if dirty and self.parent is not None:
                self.parent.dirty = True

    @property
    def rectangle(self) -> Rectangle:
        """
        :return: The position of this widget in it's parent coordinates.
        """
        return self._rectangle

    @rectangle.setter
    def rectangle(self, rectangle: Rectangle):
        self._rectangle = rectangle

    def position(self, anchor: Anchor) -> AnchoredPoint:
        """
        :return: The position of the specified point in the parent's coordinates.
        """
        return self._rectangle.position(anchor)

    def set_position(self, point: AnchoredPoint):
        """
        Moves this widget while retaining it's size.
        """
        self.rectangle = Rectangle(point, self.size)

    @property
    def size(self) -> Size:
        return self.rectangle.size

    def set_size(self, size: Size, anchor: Anchor):
        self.rectangle = Rectangle(self.rectangle.position(anchor), size)

    @property
    def background(self) -> Optional[Color]:
        return self._background

    @background.setter
    def background(self, background: Optional[Color]):
        self._background = background
        self.dirty = True

    @property
    def foreground(self) -> Color:
        return self._foreground

    @foreground.setter
    def foreground(self, foreground: Color):
        self._foreground = foreground
        self.dirty = True

    def paint(self, ctx: Context):
        """
        Renders this widget.
        :param ctx: Cairo context.
        """
        if self.background:
            ctx.set_source_rgba(*self.background)
            self.paint_background(ctx)
        ctx.set_source_rgba(*self.foreground)
        self.paint_foreground(ctx)
        self.dirty = False

    @abstractmethod
    def paint_foreground(self, ctx: Context):
        """
        Renders this widget. Is called by Widget.paint.
        """
        return

    def paint_background(self, ctx: Context):
        """
        Renders the background of this widget. Is called by Widget.paint.
        """
        ctx.rectangle(0, 0, *self.size)
        ctx.fill()

    @property
    def app(self) -> AppWidget:
        """
        :return: The App object which is the root of the widget hierarchy.
        """
        return self.parent.app

    def repaint(self):
        """
        Sets the dirty flag.
        """
        self.dirty = True

    @property
    def preferred_size(self) -> Size:
        """
        :return: If overridden, the Size this widget should have to fit all it's content.
        """
        return self.size

    def on_key_down(self, key: G19Key) -> bool:
        """
        Receives key down events.
        :param key: The pressed key.
        :return: If True, the event will not be received by further widgets.
        """
        return False

    def on_key_up(self, key: G19Key) -> bool:
        """
        Receives key up events.
        :param key: The released key.
        :return: If True, the event will not be received by further widgets.
        """
        return False

    @property
    def left(self) -> float:
        """
        :return: Left edge of this widget in the parent's coordinates.
        """
        return self._rectangle.left

    @property
    def right(self) -> float:
        """
        :return: Right edge of this widget in the parent's coordinates.
        """
        return self._rectangle.right

    @property
    def top(self) -> float:
        """
        :return: Top edge of this widget in the parent's coordinates.
        """
        return self._rectangle.top

    @property
    def bottom(self) -> float:
        """
        :return: Bottom edge of this widget in the parent's coordinates.
        """
        return self._rectangle.bottom

    @property
    def width(self) -> float:
        return self._rectangle.width

    def set_width(self, width: float, anchor: HAnchor):
        self.rectangle = Rectangle(self.position(VAnchor.TOP + anchor),
                                   Size(width, self.width))

    @property
    def height(self) -> float:
        return self._rectangle.height

    def set_height(self, height: float, anchor: VAnchor):
        self.rectangle = Rectangle(self.position(anchor + HAnchor.LEFT),
                                   Size(self.width, height))
        self.dirty = True

    def __str__(self) -> str:
        return f"{self.__class__.__name__}(rectangle={self.rectangle}, background={self.background}, " \
               f"foreground={self.foreground})"