Change mydays to maegirls.
[maegirls] / trunk / src / graph.py
1 #!/usr/bin/env python
2 # coding=UTF-8
3
4 # Copyright (C) 2010 Stefanos Harhalakis
5 #
6 # This file is part of maegirls.
7 #
8 # maegirls is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # maegirls is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with maegirls.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 # $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
22
23 __version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
24
25 from PyQt4.QtGui import *
26 from PyQt4.QtCore import *
27
28 import time
29
30 import algo
31
32 class DaysGraph(QWidget):
33     def __init__(self, algo_, *args, **kwargs):
34         QWidget.__init__(self, *args, **kwargs)
35
36         self.min_day_width=20
37         self.max_day_width=50
38         self.day_width=50
39         self.algo=algo_
40
41         self.first_day=algo.today()
42
43         self.last_x=None
44         self.last_y=None
45
46     def setAlgo(self, algo_):
47         self.algo=algo_
48
49     def reset(self):
50         self.first_day=algo.today()
51         self.repaint()
52
53     def paintEvent(self, event):
54         w0=self.width()
55         h=self.height()
56
57         gray=0x22
58
59         days=int(w0/self.day_width)
60         days=int((days)/2)*2 + 3
61         w=days*self.day_width
62
63         # Now calculate the x0
64         x0=int((w0-w)/2)
65
66         painter=QPainter(self)
67
68         # This makes it sloooooooooooow
69 #       painter.setRenderHints(painter.Antialiasing \
70 #           | painter.TextAntialiasing \
71 #           | painter.HighQualityAntialiasing
72 #           )
73
74         # common day outline
75         colol=QColor(0x66,0x66,0x66)
76         penol=QPen(colol)
77
78         # outline of the small block 
79         colblockol=Qt.white
80         penblockol=QPen(colblockol)
81
82         # Current day outline
83         colcur=Qt.white
84         pencur=QPen(colcur, 3)
85
86         # Text color
87         coltxt=Qt.white
88         pentxt=QPen(coltxt)
89
90         # Font
91         fontdate=QFont()
92         fontdate.setPointSizeF(fontdate.pointSizeF()*0.5)
93         fontdate_m=QFontMetrics(fontdate)
94
95         # Last bounding rectagle of the text
96         last_br=QRect(0,0,4,4)
97
98         today=algo.today()
99
100         for d in xrange(days):
101             # This will cause the first_day to be at the center
102             curday=self.first_day+d - int(days/2)
103             x=x0+d*self.day_width
104             y=0
105
106             h2=h-50
107
108             st=self.algo.status(curday)
109
110             if st['status']=='ok':
111                 colbg=QColor(gray, gray, gray)
112                 brbg=QBrush(colbg)
113
114                 painter.setPen(penol)
115                 painter.fillRect(x, y, self.day_width-1, h2-1, brbg) 
116             elif st['status']=='red' or \
117                 st['status']=='blue' or \
118                 st['status']=='green':
119                 if st['status']=='red':
120                     perc=1.0*st['day'] / (st['len'])
121                     colbg=QColor(
122                         0xff - int((0xff - gray - gray) * perc),
123                         int(gray * perc),
124                         int(gray * perc))
125                     colblock=Qt.red
126                 elif st['status']=='blue':
127                     perc=1.0*(st['day']+1) / (st['len'])
128                     colbg=QColor(
129                         gray,
130                         gray,
131                         gray + ((0xff-gray) * perc))
132                     colblock=Qt.blue
133                 elif st['status']=='green':
134                     perc=1.0*(st['day']+1) / (st['len'])
135                     colbg=QColor(
136                         gray,
137                         gray + ((0xff-gray) * perc),
138                         gray)
139                     colblock=Qt.green
140                 brbg=QBrush(colbg)
141                 painter.fillRect(x, y, self.day_width-1, h2-1, brbg) 
142
143                 # Show a nice small box for interesting days
144                 boxw=20
145                 boxh=10
146
147                 # Ensure that boxw is sane - e.g. not larger than
148                 # the current day_width
149                 boxw2=self.day_width-10
150                 boxw=min(boxw, boxw2)
151
152                 x1=x+int((self.day_width-boxw)/2)
153
154                 if st['status']=='red':
155                     y1=y + int(perc * h2*2/3) + 20
156                 elif st['status']=='blue' or st['status']=='green':
157                     y1=y + int(h2*2/3) - int(perc * h2*2/3) + 20
158
159                 brbg=QBrush(colblock)
160                 painter.fillRect(x1, y1, boxw, boxh, brbg)
161                 painter.setPen(colblockol)
162                 painter.drawRect(x1, y1, boxw, boxh)
163
164             painter.setPen(colol)
165             painter.drawRect(x, y, self.day_width, h2-1)
166
167             # Highlight today
168             if curday==today:
169                 painter.setPen(pencur)
170                 painter.drawRect(x+2,y+2,self.day_width-4, h2-5)
171
172             date=time.localtime(curday*86400+30000)
173
174             y2=h2
175
176             txt="%d.%02d\n%d" % (date.tm_mday, date.tm_mon, date.tm_year)
177
178             # br is used to determine overlaps
179             br=fontdate_m.boundingRect(txt)
180             br.translate(x,y2)
181
182             colnum=1
183             while colnum*self.day_width < br.width():
184                 colnum+=2
185
186             # If this intersects with the previous day then it is ok
187             if br.intersects(last_br):
188                 continue
189             last_br=br
190
191             # br2 is the true bounding rectangle
192             # The width calculation helps with alignment
193             br2=QRect(x-((colnum-1)/2 * self.day_width), y2,
194                 self.day_width*colnum, fontdate_m.height()*2)
195
196             # Draw it
197             painter.setFont(fontdate)
198             painter.setPen(pentxt)
199             painter.drawText(br2, Qt.AlignCenter | Qt.AlignTop, txt)
200
201     # Handle both horizontal and vertical movement
202     def mouseMoveEvent(self, event):
203         pos=event.pos()
204         x=pos.x()
205         y=pos.y()
206
207         torepaint=False
208
209         if self.last_x==None:
210             self.last_x=x
211             self.last_y=y
212
213         # Vertical movement - that much pixels account for one zoom
214         y_step=50
215
216         # First look horizontal movement
217         # If there was movement the reset vertical movement
218         dx=x-self.last_x
219         if dx>0:
220             days=int(dx/self.day_width)
221         else:
222             days=-int(abs(dx)/self.day_width)
223
224         if days!=0:
225             self.first_day-=days
226             self.last_x+=(days*self.day_width)
227             self.last_y=y       # Reset vertical movement
228             torepaint=True
229
230         # Now look for vertical movement
231         dy=y-self.last_y
232         if dy>0:
233             zoom=int(dy/y_step)
234         else:
235             zoom=-int(abs(dy)/y_step)
236
237         if zoom!=0:
238             t=self.day_width+zoom
239             if t<self.min_day_width:
240                 t=self.min_day_width
241             elif t>self.max_day_width:
242                 t=self.max_day_width
243             #self.day_width-=zoom
244             self.day_width=t
245
246             self.last_y+=zoom*y_step
247             self.last_x=x       # Reset horizontal movement
248             torepaint=True
249         
250         if torepaint:
251             self.repaint()
252
253     def mousePressEvent(self, event):
254         pos=event.pos()
255         self.last_x=pos.x()
256         self.last_y=pos.y()
257
258     def sizeHint(self):
259         return(QSize(100,50))
260
261 # vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
262