Skip to content

Timeline

Qt Base Class: QWidget

Signature: QWidget(self, parent: Optional[PySide6.QtWidgets.QWidget] = None, f: PySide6.QtCore.Qt.WindowType = Default(Qt.WindowFlags)) -> None

Base classes

Name Children Inherits
Widget
prettyqt.widgets.widget

⋔ Inheritance diagram

graph TD
  1473367132128["custom_widgets.Timeline"]
  1473293661888["widgets.Widget"]
  1473293688240["widgets.WidgetMixin"]
  1473299815024["core.ObjectMixin"]
  140713234304496["builtins.object"]
  1473245548480["gui.PaintDeviceMixin"]
  1473290849680["QtWidgets.QWidget"]
  1473288842240["QtCore.QObject"]
  1473291690208["Shiboken.Object"]
  1473300082368["QtGui.QPaintDevice"]
  1473293661888 --> 1473367132128
  1473293688240 --> 1473293661888
  1473299815024 --> 1473293688240
  140713234304496 --> 1473299815024
  1473245548480 --> 1473293688240
  140713234304496 --> 1473245548480
  1473290849680 --> 1473293661888
  1473288842240 --> 1473290849680
  1473291690208 --> 1473288842240
  140713234304496 --> 1473291690208
  1473300082368 --> 1473290849680
  1473291690208 --> 1473300082368

🛈 DocStrings

Bases: Widget

Source code in prettyqt\custom_widgets\timeline.py
class Timeline(widgets.Widget):
    position_changed = core.Signal(int)
    selection_changed = core.Signal(VideoSample)

    def __init__(self, duration: int, length: int):
        super().__init__()
        self.set_title("Timeline")
        self.duration = duration
        self.length = length

        # Set variables
        self.set_background_color(BACKGROUND_COLOR)
        self.set_text_color(TEXT_COLOR)
        self.set_text_font(FONT)
        self._position = None
        self.pointer_time_pos = 0.0
        self.selected_sample = None
        self._clicking = False  # Check if mouse left button is being pressed
        self._is_in = False  # check if user is in the widget
        self.video_samples: list[VideoSample] = []  # List of video samples
        self.setMouseTracking(True)  # Mouse events
        self.setAutoFillBackground(True)  # background
        self.setGeometry(300, 300, self.length, 200)

        # Set Background
        with self.edit_palette() as pal:
            pal.set_color("window", self.background_color)

    def __len__(self):
        return len(self.video_samples)

    def __getitem__(self, index: int) -> VideoSample:
        return self.video_samples[index]

    def __setitem__(self, index: int, value: VideoSample):
        self.video_samples[index] = value

    def __delitem__(self, index: int):
        del self.video_samples[index]

    def __add__(self, other: VideoSample) -> Timeline:
        self.add(other)
        return self

    @classmethod
    def setup_example(cls):
        return cls(duration=60, length=60)

    def add_sample(
        self,
        duration: int,
        color: datatypes.ColorType = "yellow",
        picture: gui.QPixmap | None = None,
    ) -> VideoSample:
        sample = VideoSample(duration, color, picture)
        self.add(sample)
        return sample

    def add(self, sample: VideoSample):
        self.video_samples.append(sample)

    def paintEvent(self, event):
        # Draw time
        scale = self.get_scale()
        with gui.Painter(self) as qp:
            qp.set_color(self.text_color)
            qp.setFont(self.text_font)
            qp.use_antialiasing()
            w = 0
            width = self.width()
            while (w := w + 100) <= width:
                msecs = int(w * scale * 1000)
                time_string = str(core.Time(0, 0, 0, 0).add_msecs(msecs))
                rect = core.Rect(w - 50, 0, 100, 100)
                qp.drawText(rect, constants.ALIGN_H_CENTER, time_string)
            # Draw down line
            qp.set_pen(color=PEN_COLOR, width=5)
            qp.drawLine(0, 40, width, 40)

            # Draw dash lines
            point = 0
            qp.set_pen(color=self.text_color)
            qp.drawLine(0, 40, width, 40)
            while point <= width:
                y2 = 30 if point % 30 != 0 else 20
                qp.drawLine(3 * point, 40, 3 * point, y2)
                point += 10

            if self._position is not None and self._is_in:
                x_pos = int(self._position.x())
                qp.drawLine(x_pos, 0, x_pos, 40)

            poly = gui.Polygon()
            if self._position is not None:
                val = int(self.pointer_time_pos / self.get_scale())
                line = core.Line(val, 40, val, self.height())
                poly.add_points((val - 10, 20), (val + 10, 20), (val, 40))
            else:
                line = core.Line(0, 0, 0, self.height())
                poly.add_points((-10, 20), (10, 20), (0, 40))

            # Draw samples
            t = 0.0
            for sample in self.video_samples:
                scaled_dur = sample.duration / scale
                scaled_t = t / scale
                t += sample.duration
                # Clear clip path
                with qp.clip_path() as path:
                    rect = core.RectF(scaled_t, 50, scaled_dur, 200)
                    path.addRoundedRect(rect, 10, 10)

                # Draw sample
                path = gui.PainterPath()
                qp.set_pen(color=sample.color)
                rect = core.RectF(scaled_t, 50, scaled_dur, 50)
                path.addRoundedRect(rect, 10, 10)
                sample.start_pos = scaled_t
                sample.end_pos = scaled_t + scaled_dur
                qp.fillPath(path, sample.color)
                qp.drawPath(path)

                # Draw preview pictures
                if sample.picture is None:
                    continue
                pic_width = sample.picture.size().width()
                if pic_width < scaled_dur:
                    width = float(pic_width)
                    pic = sample.picture
                else:
                    width = scaled_dur
                    pic = sample.picture.copy(0, 0, int(scaled_dur), 45)
                with qp.clip_path() as path:
                    rect = core.RectF(scaled_t, 52.5, width, 45)
                    path.addRoundedRect(rect, 10, 10)
                qp.drawPixmap(int(scaled_t), int(52.5), int(width), 45, pic)

            # Clear clip path
            with qp.clip_path() as path:
                path.add_rect(self.rect())

            # Draw pointer
            qp.set_color(PEN_COLOR)
            qp.set_brush(PEN_COLOR)

            qp.drawPolygon(poly)
            qp.drawLine(line)

    def mouseMoveEvent(self, e):
        self._position = e.position()

        # if mouse is being pressed, update pointer
        if self._clicking:
            x = self._position.x()
            self.position_changed.emit(x)
            self._check_selection(x)
            self.pointer_time_pos = x * self.get_scale()

        self.update()

    def mousePressEvent(self, e):
        if e.button() == constants.MouseButton.LeftButton:
            x = e.position().x()
            self.position_changed.emit(x)
            self.pointer_time_pos = x * self.get_scale()

            self._check_selection(x)

            self.update()
            self._clicking = True  # Set clicking check to true

    def mouseReleaseEvent(self, e):
        if e.button() == constants.MouseButton.LeftButton:
            self._clicking = False  # Set clicking check to false

    def enterEvent(self, e):
        self._is_in = True

    def leaveEvent(self, e):
        self._is_in = False
        self.update()

    def _check_selection(self, x: int):
        # Check if user clicked in video sample
        for sample in self.video_samples:
            if sample.start_pos < x < sample.end_pos:
                sample.color = gui.Color(PEN_COLOR)
                if self.selected_sample is not sample:
                    self.selected_sample = sample
                    self.selection_changed.emit(sample)
            else:
                sample.color = sample.def_color

    def get_scale(self) -> float:
        return self.duration / self.width()

    def set_background_color(self, color: datatypes.ColorType):
        color = colors.get_color(color)
        self.background_color = color

    def set_text_color(self, color: datatypes.ColorType):
        color = colors.get_color(color)
        self.text_color = color

    def set_text_font(self, font: gui.QFont):
        self.text_font = font

⌗ Property table

Qt Property Type Doc
objectName QString
modal bool
windowModality Qt::WindowModality
enabled bool
geometry QRect
frameGeometry QRect
normalGeometry QRect
x int
y int
pos QPoint
frameSize QSize
size QSize
width int
height int
rect QRect
childrenRect QRect
childrenRegion QRegion
sizePolicy QSizePolicy
minimumSize QSize
maximumSize QSize
minimumWidth int
minimumHeight int
maximumWidth int
maximumHeight int
sizeIncrement QSize
baseSize QSize
palette QPalette
font QFont
cursor QCursor
mouseTracking bool
tabletTracking bool
isActiveWindow bool
focusPolicy Qt::FocusPolicy
focus bool
contextMenuPolicy Qt::ContextMenuPolicy
updatesEnabled bool
visible bool
minimized bool
maximized bool
fullScreen bool
sizeHint QSize
minimumSizeHint QSize
acceptDrops bool
windowTitle QString
windowIcon QIcon
windowIconText QString
windowOpacity double
windowModified bool
toolTip QString
toolTipDuration int
statusTip QString
whatsThis QString
accessibleName QString
accessibleDescription QString
layoutDirection Qt::LayoutDirection
autoFillBackground bool
styleSheet QString
locale QLocale
windowFilePath QString
inputMethodHints QFlags

🖼 Screenshot