Use the palette's window color for drawing text.
[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.update()
52
53     def paintEvent(self, event):
54         palette=self.palette()
55
56         w0=self.width()
57         h=self.height()
58
59         gray=0x22
60
61         days=int(w0/self.day_width)
62         days=int((days)/2)*2 + 3
63         w=days*self.day_width
64
65         # Now calculate the x0
66         x0=int((w0-w)/2)
67
68         painter=QPainter(self)
69
70         # This makes it sloooooooooooow
71 #       painter.setRenderHints(painter.Antialiasing \
72 #           | painter.TextAntialiasing \
73 #           | painter.HighQualityAntialiasing
74 #           )
75
76         # common day outline
77         colol=QColor(0x66,0x66,0x66)
78         penol=QPen(colol)
79
80         # outline of the small block 
81         colblockol=Qt.white
82         penblockol=QPen(colblockol)
83
84         # Current day outline
85         colcur=Qt.white
86         pencur=QPen(colcur, 3)
87
88         # Text color
89         #coltxt=Qt.white
90         coltxt=palette.color(palette.WindowText)
91         pentxt=QPen(coltxt)
92
93         # Font
94         fontdate=QFont()
95         fontdate.setPointSizeF(fontdate.pointSizeF()*0.5)
96         fontdate_m=QFontMetrics(fontdate)
97
98         # Last bounding rectagle of the text
99         last_br=QRect(0,0,4,4)
100
101         today=algo.today()
102
103         for d in xrange(days):
104             # This will cause the first_day to be at the center
105             curday=self.first_day+d - int(days/2)
106             x=x0+d*self.day_width
107             y=0
108
109             h2=h-50
110
111             st=self.algo.status(curday)
112
113             if st['status']=='ok':
114                 colbg=QColor(gray, gray, gray)
115                 brbg=QBrush(colbg)
116
117                 painter.setPen(penol)
118                 painter.fillRect(x, y, self.day_width-1, h2-1, brbg) 
119             elif st['status']=='red' or \
120                 st['status']=='blue' or \
121                 st['status']=='green':
122                 if st['status']=='red':
123                     perc=1.0*st['day'] / (st['len'])
124                     colbg=QColor(
125                         0xff - int((0xff - gray - gray) * perc),
126                         int(gray * perc),
127                         int(gray * perc))
128                     colblock=Qt.red
129                 elif st['status']=='blue':
130                     perc=1.0*(st['day']+1) / (st['len'])
131                     colbg=QColor(
132                         gray,
133                         gray,
134                         gray + ((0xff-gray) * perc))
135                     colblock=Qt.blue
136                 elif st['status']=='green':
137                     perc=1.0*(st['day']+1) / (st['len'])
138                     colbg=QColor(
139                         gray,
140                         gray + ((0xff-gray) * perc),
141                         gray)
142                     colblock=Qt.green
143                 brbg=QBrush(colbg)
144                 painter.fillRect(x, y, self.day_width-1, h2-1, brbg) 
145
146                 # Show a nice small box for interesting days
147                 boxw=20
148                 boxh=10
149
150                 # Ensure that boxw is sane - e.g. not larger than
151                 # the current day_width
152                 boxw2=self.day_width-10
153                 boxw=min(boxw, boxw2)
154
155                 x1=x+int((self.day_width-boxw)/2)
156
157                 if st['status']=='red':
158                     y1=y + int(perc * h2*2/3) + 20
159                 elif st['status']=='blue' or st['status']=='green':
160                     y1=y + int(h2*2/3) - int(perc * h2*2/3) + 20
161
162                 brbg=QBrush(colblock)
163                 painter.fillRect(x1, y1, boxw, boxh, brbg)
164                 painter.setPen(colblockol)
165                 painter.drawRect(x1, y1, boxw, boxh)
166
167             painter.setPen(colol)
168             painter.drawRect(x, y, self.day_width, h2-1)
169
170             # Highlight today
171             if curday==today:
172                 painter.setPen(pencur)
173                 painter.drawRect(x+1,y+3,self.day_width-4, h2-7)
174
175             date=time.localtime(curday*86400+30000)
176
177             y2=h2
178
179             day=QLocale().dayName(date.tm_wday+1, QLocale.ShortFormat)
180             txt="%s\n%d.%02d\n%d" % \
181                 (day, date.tm_mday, date.tm_mon, date.tm_year)
182
183             # br is used to determine overlaps
184             br=fontdate_m.boundingRect(txt)
185             br.translate(x,y2)
186
187             colnum=1
188             while colnum*self.day_width < br.width():
189                 colnum+=2
190
191             # If this intersects with the previous day then it is ok
192             if br.intersects(last_br):
193                 continue
194             last_br=br
195
196             # br2 is the true bounding rectangle
197             # The width calculation helps with alignment
198             br2=QRect(x-((colnum-1)/2 * self.day_width), y2,
199                 self.day_width*colnum, fontdate_m.height()*3)
200
201             # Draw it
202             painter.setFont(fontdate)
203             painter.setPen(pentxt)
204             painter.drawText(br2, Qt.AlignCenter | Qt.AlignTop, txt)
205
206         # Outer border
207
208 # Try 1
209 #       for x in xrange(5, -1, -1):
210 #           c=0x11*(x+1)
211 #           colobg=QColor(c, c, c)
212 #           penobg=QPen(colobg, 1)
213 #           painter.setPen(penobg)
214 #           painter.drawRect(x, x, self.width()-(2*x)-1, h2-(2*x)-1)
215
216 # Try 2
217 #       colobg=Qt.black
218 #       w=2
219 #       penobg=QPen(colobg, w)
220 #       painter.setPen(penobg)
221 #       painter.drawRect(w/2, w/2, self.width()-w, h2-w)
222
223     # Handle both horizontal and vertical movement
224     def mouseMoveEvent(self, event):
225         pos=event.pos()
226         x=pos.x()
227         y=pos.y()
228
229         torepaint=False
230
231         if self.last_x==None:
232             self.last_x=x
233             self.last_y=y
234
235         # Vertical movement - that much pixels account for one zoom
236         y_step=50
237
238         # First look horizontal movement
239         # If there was movement the reset vertical movement
240         dx=x-self.last_x
241         if dx>0:
242             days=int(dx/self.day_width)
243         else:
244             days=-int(abs(dx)/self.day_width)
245
246         if days!=0:
247             self.first_day-=days
248             self.last_x+=(days*self.day_width)
249             self.last_y=y       # Reset vertical movement
250             torepaint=True
251
252         # Now look for vertical movement
253         dy=y-self.last_y
254         if dy>0:
255             zoom=int(dy/y_step)
256         else:
257             zoom=-int(abs(dy)/y_step)
258
259         if zoom!=0:
260             t=self.day_width+zoom
261             if t<self.min_day_width:
262                 t=self.min_day_width
263             elif t>self.max_day_width:
264                 t=self.max_day_width
265             #self.day_width-=zoom
266             self.day_width=t
267
268             self.last_y+=zoom*y_step
269             self.last_x=x       # Reset horizontal movement
270             torepaint=True
271         
272         if torepaint:
273             self.update()
274
275     def mousePressEvent(self, event):
276         pos=event.pos()
277         self.last_x=pos.x()
278         self.last_y=pos.y()
279
280     def sizeHint(self):
281         return(QSize(100,50))
282
283 # vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
284