summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoïc Guégan <loic.guegan@mailbox.org>2024-09-16 12:25:41 +0200
committerLoïc Guégan <loic.guegan@mailbox.org>2024-09-16 12:25:41 +0200
commit79ce6bc894120527729bb282773a77a3c9cbaa2b (patch)
tree2ca8590a99d5f43f4838acd3b3abb5b5d98ddeed
Minor changes
-rw-r--r--.gitignore3
-rw-r--r--notes.org6
-rw-r--r--tropical/__init__.py1
-rw-r--r--tropical/calstate.py49
-rw-r--r--tropical/db.py37
-rw-r--r--tropical/qt/__init__.py1
-rw-r--r--tropical/qt/caldrawer.py192
-rw-r--r--tropical/qt/designer/MainWindow.ui81
-rw-r--r--tropical/qt/eventdrawer.py61
-rw-r--r--tropical/qt/mainwindow.py41
-rwxr-xr-xtropical/tropical.py19
11 files changed, 491 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a990e4e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+env/
+**/__pycache__
+**/*.db \ No newline at end of file
diff --git a/notes.org b/notes.org
new file mode 100644
index 0000000..6c60d19
--- /dev/null
+++ b/notes.org
@@ -0,0 +1,6 @@
+
+
+
+
+- [[https://doc.qt.io/qtforpython-6/PySide6/QtGui/QPainterPath.html][Qt6 Python doc]]
+- [[https://stackoverflow.com/questions/9249842/how-does-qt-draw-a-border-around-a-rectangle][How rectangle border drawn]]
diff --git a/tropical/__init__.py b/tropical/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tropical/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tropical/calstate.py b/tropical/calstate.py
new file mode 100644
index 0000000..2a760a6
--- /dev/null
+++ b/tropical/calstate.py
@@ -0,0 +1,49 @@
+
+import calendar
+from datetime import date, timedelta
+
+class CalState:
+
+ def __init__(self):
+ self.gotoToday()
+ self.firstWeekDay=0
+
+ def setFirstWeekDay(self, i):
+ self.firstWeekDay=i
+
+ def goto(self, year, month, day):
+ self.year=year
+ self.month=month
+ self.day=day
+
+ def gotoToday(self):
+ today = date.today()
+ self.goto(today.year, today.month, today.day)
+
+ def gotoNextWeek(self):
+ day=date(self.year,self.month,self.day) + timedelta(weeks=1)
+ self.goto(day.year,day.month, day.day)
+
+ def gotoPrevWeek(self):
+ day=date(self.year,self.month,self.day) - timedelta(weeks=1)
+ self.goto(day.year,day.month, day.day)
+
+ def getMonthDays(self):
+ cal=calendar.Calendar()
+ days=list()
+ for day in cal.itermonthdays4(self.year, self.month):
+ days.append(day)
+ return days
+
+ def getWeekDays(self):
+ daysMonth=self.getMonthDays()
+ daysWeek=list()
+ for year, month, day, count in daysMonth:
+ daysWeek.append((year,month,day,count))
+ if len(daysWeek) >= 7:
+ # Check if day within the week
+ for yy, mm, dd, cc in daysWeek:
+ if (yy, mm, dd) == (self.year, self.month, self.day):
+ return daysWeek
+ daysWeek.clear()
+ return None
diff --git a/tropical/db.py b/tropical/db.py
new file mode 100644
index 0000000..15e083b
--- /dev/null
+++ b/tropical/db.py
@@ -0,0 +1,37 @@
+import sqlite3, time, socket
+
+
+class CalDB:
+ __DBVERSION__="1"
+
+ def __init__(self, dbPath):
+ self.path=dbPath
+ self.con=sqlite3.connect(dbPath)
+ self.cur=self.con.cursor()
+
+ # Init database
+ res=self.cur.execute('SELECT name FROM sqlite_master WHERE type="table" AND name="infos";')
+ if res.fetchone() is None:
+ self.initDB()
+
+ def initDB(self):
+ # Infos table
+ self.cur.execute("CREATE TABLE infos(name UNIQUE, value TEXT)")
+ self.cur.execute('INSERT INTO infos VALUES("dbversion", "'+CalDB.__DBVERSION__+'")')
+ self.cur.execute('INSERT INTO infos VALUES("creation", "'+str(time.time())+'")')
+ self.cur.execute('INSERT INTO infos VALUES("created_on", "'+socket.gethostname()+'")')
+ # Calendars table
+ self.cur.execute("CREATE TABLE calendars(id INTEGER PRIMARY KEY, name TEXT, description TEXT, color TEXT)")
+ # Events table
+ self.cur.execute("CREATE TABLE events(id INTEGER PRIMARY KEY, name TEXT, calendar INTEGER, description TEXT, start REAL, end REAL, repeat TEXT, frequency INTEGER, FOREIGN KEY(calendar) REFERENCES calendars(id))")
+ self.con.commit()
+
+ def keyExists(self, db, key):
+ res=self.cur.execute("SELECT * FROM {} WHERE id={}".format(db,key))
+ return not res.fetchone() is None
+
+ def addEvent(self, event):
+ """
+ Event format: { name: str, calendar: int, desc: str, start: float, end: float, repeat: str, frequency: int }
+ """
+ pass
diff --git a/tropical/qt/__init__.py b/tropical/qt/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tropical/qt/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tropical/qt/caldrawer.py b/tropical/qt/caldrawer.py
new file mode 100644
index 0000000..7e6b3fc
--- /dev/null
+++ b/tropical/qt/caldrawer.py
@@ -0,0 +1,192 @@
+
+from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QSizePolicy
+from PyQt6 import uic, QtGui, QtCore
+from PyQt6.QtCore import Qt, QRect
+import math
+#https://forum.qt.io/topic/93327/how-can-i-use-qpainter-to-paint-on-qgraphicsview/5
+#https://www.pythonguis.com/tutorials/pyqt6-bitmap-graphics/#qpainter
+
+# to solve the fit problem: https://stackoverflow.com/questions/61886358/qgraphicsview-fitinview-not-working-as-expected
+
+class CalQGraphicsView(QGraphicsView):
+ def __init__(self, scene):
+ super().__init__(None)
+ self.setScene(scene)
+
+ def resizeEvent(self, event):
+ self.fitInView(self.sceneRect(), Qt.AspectRatioMode.IgnoreAspectRatio)
+
+
+class CalDrawerScene(QGraphicsScene):
+ def __init__(self, calState):
+ self.gridWidth=2
+ self.daysLabelBG="#dddddd"
+ self.eventsLabelBG="#36e364"
+ super().__init__(None)
+ self.showWeekends=True
+ self.daysRect=list()
+ self.eventsRect=list()
+ self.calState=calState
+ self.daysNames=["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]
+
+ def drawForeground(self, painter, rect):
+ origXF, origYF, widthF, heightF = rect.getRect()
+ origXI, origYI, widthI, heightI = (int(origXF),int(origYF),int(widthF),int(heightF))
+ self.defaultBrush=painter.brush()
+ self.daysRect.clear()
+ self.eventsRect.clear()
+ daysNamesH=self.drawDaysName(painter, origXI, origYI, widthI, heightI)
+ self.drawGrid(painter, origXI, origYI+daysNamesH, widthI, heightI-daysNamesH)
+ self.drawDaysLabel(painter)
+ self.drawEvents(painter)
+
+ def drawDaysName(self, painter, x, y, width, height):
+ # Init Pen
+ pen=QtGui.QPen()
+ pen.setWidth(self.gridWidth)
+ pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin)
+ po=int(self.gridWidth/2) # Pen offset
+ painter.setPen(pen)
+ # Init Brush
+ painter.setBrush(Qt.BrushStyle.NoBrush)
+ # Setup dimensions
+ weekLength=7
+ font=painter.font()
+ metric=QtGui.QFontMetrics(font);
+ labelH=metric.boundingRect("".join(self.daysNames)).height()
+ margin=0
+ offsetY=int(labelH/2)+int(labelH/4)+margin
+ w=int((width-po*2)/weekLength)
+ h=int(labelH)
+ for i in range(0, 7):
+ labelW=metric.boundingRect(self.daysNames[i]).width()
+ offsetX=int(w/2-labelW/2)
+# painter.drawText(x+offsetX+w*i,y+offsetY,self.daysNames[i])
+ painter.drawText(x+w*i,y,w,h,Qt.AlignmentFlag.AlignHCenter,self.daysNames[i])
+ rect=QRect(x+po+w*i,y+po,w,h) # Draw grid
+ painter.drawRect(rect)
+ return labelH
+
+ def drawGrid(self,painter, x, y, width, height):
+ # Init Pen
+ pen=QtGui.QPen()
+ pen.setWidth(self.gridWidth)
+ pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin)
+ po=int(self.gridWidth/2) # Pen offset
+ painter.setPen(pen)
+ # Init Brush
+ painter.setBrush(Qt.BrushStyle.NoBrush)
+ # Setup dimensions
+ weekLength=7
+ if not self.showWeekends:
+ weekLength=5
+ daysCount=weekLength*6
+
+
+ # brush=QtGui.QBrush()
+ # brush.setColor(QtGui.QColor("#0000FF"))
+ # brush.setStyle(Qt.BrushStyle.SolidPattern)
+
+
+ w=int((width-po*2)/weekLength)
+ h=int((height-po*2)/6)
+ row=0
+ col=0
+ self.daysRect.clear()
+ while row*weekLength+col<daysCount:
+ rect=QRect(x+po+w*col,y+po+row*h,w,h) # Draw grid
+ rectInside=QRect(rect.x()+po,rect.y()+po,rect.width()-po*2,rect.height()-po*2) # Area within grid (within stroke)
+ self.daysRect.append(rectInside) # Store this region for later drawing
+ # if col==0 and row == 0:
+ # painter.setBrush(brush)
+ # painter.setPen(Qt.PenStyle.NoPen)
+ # painter.drawRect(QRect(b.x(),b.y(),b.width(),b.height()))
+ # painter.setPen(self.gridPen)
+ # painter.setBrush(self.defaultBrush)
+ painter.drawRect(rect)
+ col+=1
+ if col==weekLength:
+ col=0
+ row+=1
+
+ def drawDaysLabel(self,painter):
+ # Init Pen
+ pen=QtGui.QPen()
+ pen.setWidth(self.gridWidth)
+ pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin)
+ po=int(self.gridWidth/2) # Pen offset
+ # Init Brush
+ brush=QtGui.QBrush()
+ brush.setColor(QtGui.QColor(self.daysLabelBG))
+ brush.setStyle(Qt.BrushStyle.SolidPattern)
+ painter.setBrush(brush)
+ # Init various things
+ days=self.calState.getMonthDays()
+ font=painter.font()
+ metric=QtGui.QFontMetrics(font);
+ labelH=metric.boundingRect("1234567890").height()
+ margin=0
+ offsetY=int(labelH/2)+int(labelH/4)+margin
+ # Draw labels
+ for i in range(0,len(self.daysRect)):
+ r=self.daysRect[i]
+ d=days[i]
+ dayLabel=str(d[2])
+ labelW=metric.boundingRect(dayLabel).width()
+ offsetX=int(r.width()/2-labelW/2)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.drawRect(r.x(),r.y(),r.width(),labelH) # Remember r is within grid stroke
+ painter.setPen(pen)
+# painter.drawText(r.x()+offsetX,r.y()+offsetY,dayLabel)
+ painter.drawText(r,Qt.AlignmentFlag.AlignHCenter,dayLabel)
+ self.eventsRect.append(QRect(r.x(),r.y()+labelH+margin,r.width(),r.height()-(labelH+margin)))
+
+ def drawEvents(self,painter):
+ # Init Pen
+ pen=QtGui.QPen()
+ pen.setWidth(self.gridWidth)
+ pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin)
+ po=int(self.gridWidth/2) # Pen offset
+ # Init Brush
+ brush=QtGui.QBrush()
+ brush.setColor(QtGui.QColor(self.eventsLabelBG))
+ brush.setStyle(Qt.BrushStyle.SolidPattern)
+ painter.setBrush(brush)
+ # Init various things
+ days=self.calState.getMonthDays()
+ font=painter.font()
+ metric=QtGui.QFontMetrics(font);
+ labelH=metric.height()
+ margin=0
+ colMark=5
+ colMarkPadding=2
+ offsetY=int(labelH/2)+int(labelH/4)+margin
+ # Draw
+ po=int(self.gridWidth/2) # Pen offset
+ for r in self.eventsRect:
+ offsetX=colMark+colMarkPadding
+ painter.drawText(r.x()+po+offsetX,r.y(),r.width()-offsetX-po*2,r.height(),0,"event testddddddddddddd")
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.drawRect(r.x(),r.y(),colMark,labelH) # Remember r is within grid stroke
+ painter.setPen(pen)
+
+class CalDrawer():
+
+ def __init__(self, layout, calState):
+ self.gs=CalDrawerScene(calState)
+ self.gv=CalQGraphicsView(self.gs)
+ # Setup propertion
+ spLeft=QSizePolicy(QSizePolicy.Policy.Preferred,QSizePolicy.Policy.Preferred);
+ spLeft.setHorizontalStretch(3);
+ self.gv.setSizePolicy(spLeft);
+
+ # if jean:
+ # spLeft=QSizePolicy(QSizePolicy.Policy.Preferred,QSizePolicy.Policy.Preferred);
+ # spLeft.setHorizontalStretch(2);
+ # self.gv.setSizePolicy(spLeft);
+ # else:
+ # spLeft=QSizePolicy(QSizePolicy.Policy.Preferred,QSizePolicy.Policy.Preferred);
+ # spLeft.setHorizontalStretch(1);
+ # self.gv.setSizePolicy(spLeft);
+ layout.addWidget(self.gv)
+
diff --git a/tropical/qt/designer/MainWindow.ui b/tropical/qt/designer/MainWindow.ui
new file mode 100644
index 0000000..656646c
--- /dev/null
+++ b/tropical/qt/designer/MainWindow.ui
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>448</width>
+ <height>623</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QFrame" name="calContainer">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2"/>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton">
+ <property name="text">
+ <string>PushButton</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>448</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>File</string>
+ </property>
+ </widget>
+ <widget class="QMenu" name="menuAbout">
+ <property name="title">
+ <string>About</string>
+ </property>
+ </widget>
+ <addaction name="menuFile"/>
+ <addaction name="menuAbout"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <widget class="QToolBar" name="toolBar">
+ <property name="windowTitle">
+ <string>toolBar</string>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tropical/qt/eventdrawer.py b/tropical/qt/eventdrawer.py
new file mode 100644
index 0000000..4afcf55
--- /dev/null
+++ b/tropical/qt/eventdrawer.py
@@ -0,0 +1,61 @@
+
+from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QSizePolicy
+from PyQt6 import uic, QtGui, QtCore
+from PyQt6.QtCore import Qt, QRect
+import math
+#https://forum.qt.io/topic/93327/how-can-i-use-qpainter-to-paint-on-qgraphicsview/5
+#https://www.pythonguis.com/tutorials/pyqt6-bitmap-graphics/#qpainter
+
+# to solve the fit problem: https://stackoverflow.com/questions/61886358/qgraphicsview-fitinview-not-working-as-expected
+
+class EvtQGraphicsView(QGraphicsView):
+ def __init__(self, scene):
+ super().__init__(None)
+ self.setScene(scene)
+
+ def resizeEvent(self, event):
+ self.fitInView(self.sceneRect(), Qt.AspectRatioMode.IgnoreAspectRatio)
+
+
+class EvtDrawerScene(QGraphicsScene):
+ def __init__(self, calState):
+ self.gridWidth=2
+ super().__init__(None)
+ self.calState=calState
+
+ def drawForeground(self, painter, rect):
+ origXF, origYF, widthF, heightF = rect.getRect()
+ origXI, origYI, widthI, heightI = (int(origXF),int(origYF),int(widthF),int(heightF))
+ self.drawEvents(painter,origXI, origYI, widthI, heightI)
+
+
+ def drawEvents(self,painter,x,y,width,height):
+ # Init Pen
+ pen=QtGui.QPen()
+ pen.setWidth(self.gridWidth)
+ pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin)
+ po=int(self.gridWidth/2) # Pen offset
+ painter.setPen(pen)
+
+ painter.drawRect(x+po,y+po,width-po*2,height-po*2)
+
+class EvtDrawer():
+
+ def __init__(self, layout, calState):
+ self.gs=EvtDrawerScene(calState)
+ self.gv=EvtQGraphicsView(self.gs)
+ # Setup propertion
+ spLeft=QSizePolicy(QSizePolicy.Policy.Preferred,QSizePolicy.Policy.Preferred);
+ spLeft.setHorizontalStretch(1);
+ self.gv.setSizePolicy(spLeft);
+
+ # if jean:
+ # spLeft=QSizePolicy(QSizePolicy.Policy.Preferred,QSizePolicy.Policy.Preferred);
+ # spLeft.setHorizontalStretch(2);
+ # self.gv.setSizePolicy(spLeft);
+ # else:
+ # spLeft=QSizePolicy(QSizePolicy.Policy.Preferred,QSizePolicy.Policy.Preferred);
+ # spLeft.setHorizontalStretch(1);
+ # self.gv.setSizePolicy(spLeft);
+ layout.addWidget(self.gv)
+
diff --git a/tropical/qt/mainwindow.py b/tropical/qt/mainwindow.py
new file mode 100644
index 0000000..48da072
--- /dev/null
+++ b/tropical/qt/mainwindow.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+
+
+from PyQt6.QtWidgets import QApplication, QWidget, QMainWindow
+from PyQt6 import uic, QtGui
+from PyQt6.QtCore import Qt
+from .caldrawer import CalDrawer
+from .eventdrawer import EvtDrawer
+
+# Only needed for access to command line arguments
+import sys, os
+
+
+class MainWindow(QMainWindow):
+
+ def __init__(self,uipath, calState):
+ super(MainWindow,self).__init__()
+ uic.loadUi(uipath+"/MainWindow.ui",self)
+ self.calDrawer=CalDrawer(self.calContainer.layout(),calState)
+ self.evtDrawer=EvtDrawer(self.calContainer.layout(),calState)
+ self.calState=calState
+ self.show()
+
+ def setVersion(self,version):
+ self.statusbar.showMessage("Calanus v"+version,0)
+
+def StartApplication(version,calState):
+ path = os.path.dirname(os.path.abspath(__file__))+"/designer"
+ # You need one (and only one) QApplication instance per application.
+ # Pass in sys.argv to allow command line arguments for your app.
+ # If you know you won't use command line arguments QApplication([]) works too.
+ app = QApplication(sys.argv)
+
+ # Create a Qt widget, which will be our window.
+ window = MainWindow(path, calState)
+ window.setVersion(version)
+ window.show() # IMPORTANT!!!!! Windows are hidden by default.
+
+ # Start the event loop.
+ app.exec()
+ return window
diff --git a/tropical/tropical.py b/tropical/tropical.py
new file mode 100755
index 0000000..bb1e7e2
--- /dev/null
+++ b/tropical/tropical.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+
+#!/usr/bin/env python3
+
+import qt.mainwindow as QtCalanus
+from calstate import CalState
+from db import CalDB
+
+__VERSION__ = "0.1"
+
+
+
+
+
+if __name__ == '__main__':
+ calState=CalState()
+ QtCalanus.StartApplication(__VERSION__,calState)
+ #print(db.keyExists("calendars",1))
+#db=CalDB("sqlite.db")