I need to display more rows like this (in the direction of the red arrow), with more of those squares. Along with the squares I could also use other shapes of various colours, and the objects need to be placed in very specific coordinates. When I hover over any of those objects, it has to show a tooltip relevant to that object. The text shown, would be rendered as a graphical object, and not as text that can be selected with a mouse pointer. Basically, everything is fully graphically rendered.
As more data is generated, more rows will be added and it goes on infinitely. Obviously a scrollbar is needed.
So far, from libraries like Matplotlib, I’ve known only options of creating a graphical screen of fixed size. I considered PyQt widgets, but it doesn’t seem to have the desired functionality.
I considered HTML (since adding new rows infinitely is easy) and JavaScript, but it’s too cumbersome to export the data from Python and load and parse it in JavaScript.
Is there any way to do such a display in Python? Demonstrating one way of achieving this objective effectively would suffice.
Purpose: I’m creating this to visualize how certain foods and sleep loss lead to health issues. Displaying it like this allows me to see patterns across weeks or months. It’s not just about plotting points and text, it’s also about being able to dynamically update their color and size on clicking any of the graphical elements.
Update:
I’ve written some code to create a basic window, but I’m unable to enlarge the main window and the tooltips aren’t working. Also, given the way setGeometry
is used, I don’t think I could add rows infinitely dynamically. I’m still trying to figure it out. If y’all could help, please do.
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QScrollArea, QLabel, QToolTip
from PyQt5.QtGui import QPainter, QColor, QFont
from PyQt5.QtCore import Qt, QTimer
import random
import datetime
class CustomWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.rows = 200
self.row_height = 25
self.scroll_offset = 0
def initUI(self):
self.setGeometry(100, 100, 800, 1200)
self.setWindowTitle('Scrollable Window')
self.setMouseTracking(True)
self.setMinimumSize(1000, 1800) # Adjust the size here
self.setFixedWidth(1500)
def paintEvent(self, event):
painter = QPainter(self)
painter.setFont(QFont('Arial', 10))
start_row = max(0, int(self.scroll_offset / self.row_height) - 1)
end_row = min(start_row + int(self.height() / self.row_height) + 2, self.rows)
for i in range(start_row, end_row):
row_y = i * self.row_height - self.scroll_offset
# Displaying datetime at the left
current_time = datetime.datetime.now() + datetime.timedelta(hours=i)
time_str = current_time.strftime("%H:%M")
painter.drawText(8, row_y + 20, time_str)
# Drawing small randomly colored circles/squares
for j in range(5):
random.seed(i * j + j)
shape = random.choice(["circle", "square"])
color = QColor(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
painter.setBrush(color)
shapeSize = 5
if shape == "circle":
painter.drawEllipse(70 + j * 30, row_y + 10, shapeSize, shapeSize)
else:
painter.drawRect(70 + j * 30, row_y + 10, shapeSize, shapeSize)
def mouseMoveEvent(self, event):
for i in range(5):
if (70 + i * 30) <= event.x() <= (90 + i * 30):
row = int((event.y() + self.scroll_offset) / self.row_height)
tooltip_text="".join(random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ') for _ in range(5))
QToolTip.showText(event.globalPos(), tooltip_text, self)
break
else:
QToolTip.hideText()
def wheelEvent(self, event):
scroll_amount = 20
self.scroll_offset += -event.angleDelta().y() / 8
self.scroll_offset = max(0, min(self.scroll_offset, (self.rows * self.row_height) - self.height()))
self.update()
class ScrollableWidget(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
scroll = QScrollArea()
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
content = CustomWidget()
scroll.setWidget(content)
# Change the size of the content widget
#content.setMinimumSize(1000, 1800) # Adjust the size here
layout.addWidget(scroll)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = ScrollableWidget()
ex.show()
sys.exit(app.exec_())