Code725
Code725

Reputation: 1

Create TimeLine field with PyQt5

I need to develop a program to enter time reports. What I do is very similar to time graphics. I was only able to implement scaling, shuffling and pseudo-infinity fields. The main task is to somehow add intermediate lines between years as we scale. And add dates under each line up to minutes.

I tried to implement this by drawing a line only to the edges of the screen. That is, I took the width of the screen and divided it integerly by the distance between the lines and got the number of lines to the edges of the screen. And the distance between the lines depended on the scale. But I couldn't add sublines and make the scaling infinite like in time graphics

Code:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel
from PyQt5.QtGui import QPainter, QPen
from PyQt5.QtCore import Qt, QPoint


class DrawingApp(QMainWindow):
    def __init__(self):
        super().__init__()

        #settings
        self.drawing_widget = self.DrawingWidget(self)
        self.setCentralWidget(self.drawing_widget)

        self.status_label = QLabel("", self)
        self.status_label.setGeometry(10, 10, 200, 20)

        self.setGeometry(100, 100, 800, 600)

        self.drawing_widget.installEventFilter(self)
        self.drawing_widget.setMouseTracking(True)

        self.dragging = False
        self.drag_start_position = QPoint()
        self.was_dragged = False

    def eventFilter(self, obj, event):
        '''all mouse events'''
        if obj is self.drawing_widget:
            if event.type() == event.MouseButtonPress and event.button() == Qt.LeftButton:
                self.dragging = True
                self.drag_start_position = event.pos()
                self.was_dragged = False
                return True

            if event.type() == event.MouseMove and self.dragging:
                delta = event.pos() - self.drag_start_position
                if delta.manhattanLength() > 0:
                    self.drawing_widget.update_offset(delta)
                    self.drag_start_position = event.pos()
                    self.was_dragged = True
                return True

            if event.type() == event.MouseButtonRelease and event.button() == Qt.LeftButton:
                self.dragging = False
                if not self.was_dragged:
                    self.status_label.setText(f"Click at: ({event.pos().x()}, {event.pos().y()})")
                return True

            if event.type() == event.Wheel:
                zoom_scale = 1.1
                if event.angleDelta().y() > 0:
                    self.drawing_widget.change_scale(zoom_scale, event.pos())
                else:
                    self.drawing_widget.change_scale(1 / zoom_scale, event.pos())
                return True

        return super().eventFilter(obj, event)

    class DrawingWidget(QWidget):
        '''all drawing events'''
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setMinimumSize(800, 600)
            self.offset = QPoint(0, 0)
            self.scale = 1.0

        def update_offset(self, delta):
            self.offset += delta
            self.update()

        def change_scale(self, scale_factor, mouse_pos):
            '''change scale with align to cursors coordinate'''
            new_scale = self.scale * scale_factor
            if new_scale < 0.1:
                new_scale = 0.1
            if new_scale > 100:
                new_scale = 100

            cursor_offset = mouse_pos - self.rect().center()
            self.offset = QPoint(
                int(self.offset.x() * scale_factor - cursor_offset.x() * (scale_factor - 1)),
                #int(self.offset.y() * scale_factor - cursor_offset.y() * (scale_factor - 1))
            )
            self.scale = new_scale
            self.update()

        def paintEvent(self, event):
            painter = QPainter(self)
            offset_x, offset_y = self.offset.x(), self.offset.y()
            widget_width = self.width()
            widget_height = self.height()
            center = self.rect().center()

            scaled_size = 100 * self.scale
            sector_count = round(widget_width / scaled_size)

            offset_x %= scaled_size

            for i in range(sector_count + 2):
                # x_start = round(center.x() + offset_x) - scaled_size * i
                # x_end = round(center.x() + offset_x) + scaled_size * i

                painter.setPen(QPen(Qt.green, 2, Qt.SolidLine))

                x = round(round(center.x() + offset_x) + scaled_size * i)
                painter.drawLine(x, 20, x, widget_height - 20)

                x = round(round(center.x() + offset_x) - scaled_size * i)
                painter.drawLine(x, 20, x, widget_height - 20)


                #sublines
                # if scaled_size > 200: 
                #     subgrid_interval = scaled_size / 4
                #     for j in range(1, 4):
                #         sub_x1 = round(x_start + j * subgrid_interval)
                #         sub_x2 = round(x_end - j * subgrid_interval)

                #         painter.setPen(QPen(Qt.gray, 1, Qt.DashLine))
                #         painter.drawLine(sub_x1, 20, sub_x1, widget_height - 20)
                #         painter.drawLine(sub_x2, 20, sub_x2, widget_height - 20)


                #dates
                # painter.setPen(Qt.black)
                # painter.drawText(x_start - 10, widget_height // 2 + 5, "date")
                # painter.drawText(x_end - 10, widget_height // 2 + 5, "date")
            


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = DrawingApp()
    main_window.show()
    sys.exit(app.exec_())

Pls help me with this or suggest articles or ready-made solutions that can be adapted to my needs. Maybe someone has already encountered this problem.

Upvotes: -1

Views: 95

Answers (1)

Armali
Armali

Reputation: 19375

add dates under each line

A date can be added to each line depending on the widget's offset, e. g. for years:

    toyear = 2024+(offset_x-self.offset.x())/scaled_size
    painter.drawText(x_start - 10, widget_height // 2 + 5, "%d"%(toyear-i))
    painter.drawText(x_end - 10, widget_height // 2 + 5, "%d"%(toyear+i))

This can of course be expanded for sublines as desired.

Note that you must not limit scale to 100 if you want to zoom in beyond days.

Upvotes: 0

Related Questions