4 # Copyright (C) 2010 Stefanos Harhalakis
6 # This file is part of maegirls.
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.
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.
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/>.
21 # $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
23 __version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
25 from PyQt4.QtGui import *
26 from PyQt4.QtCore import *
32 from graph import DaysGraph
39 class ConfigDialog(QDialog):
40 def __init__(self, *args, **kwargs):
41 QDialog.__init__(self, *args, **kwargs)
43 self.editName=QLineEdit(self)
44 self.editCycle=QSpinBox(self)
45 self.editCurrent=QSpinBox(self)
46 self.editMenstr=QSpinBox(self)
47 self.editOvday=QSpinBox(self)
49 self.editCycle.setRange(10,50)
50 self.editCurrent.setRange(1,50)
51 self.editMenstr.setRange(3,7)
52 self.editOvday.setRange(-30,+30)
54 self.editCurrent.setWrapping(True)
55 self.editCycle.setSuffix(self.tr(" days"))
56 self.editMenstr.setSuffix(self.tr(' days'))
57 self.editOvday.setSuffix(self.tr(' days'))
59 self.editCycle.valueChanged.connect(self.slotEditCycleChanged)
61 self.l0=QHBoxLayout(self)
64 l1.addRow(self.tr("Name:"), self.editName)
65 l1.addRow(self.tr("Cycle length:"), self.editCycle)
66 l1.addRow(self.tr("Current day in cycle:"), self.editCurrent)
67 l1.addRow(self.tr("Duration of menstruation:"), self.editMenstr)
68 l1.addRow(self.tr("Ovulation day:"), self.editOvday)
72 spacer=QSpacerItem(20, 20, QSizePolicy.Expanding)
73 self.l0.addItem(spacer)
78 self.buttonOk=QPushButton(self)
79 self.buttonOk.setText(self.tr("OK"))
80 self.buttonOk.clicked.connect(self.slotButOk)
81 l2.addWidget(self.buttonOk)
83 spacer=QSpacerItem(20, 20, QSizePolicy.Minimum,QSizePolicy.Expanding)
86 self.setWindowTitle(self.tr("Configuration"))
89 self.name=str(self.editName.text())
90 self.cycle=self.editCycle.value()
91 self.current=self.editCurrent.value()-1
92 self.menstr=self.editMenstr.value()
93 self.ovday=self.editOvday.value()
97 def slotEditCycleChanged(self, value):
98 self.editCurrent.setMaximum(value)
100 # current starts from 0
101 def initValues(self, dt):
103 self.editName.setText(dt['name'])
104 self.editCycle.setValue(dt['cycle'])
105 self.editCurrent.setValue(dt['day0']+1)
106 self.editMenstr.setValue(dt['menstr'])
107 self.editOvday.setValue(dt['ovday'])
109 class MyMsgDialog(QDialog):
111 A Dialog to show a finger-scrollable message
115 class Koko(MyMsgDialog):
117 MyMsgDialog.__init__(....)
120 self.setWindowTitle("My title")
122 l1=QLabel("koko", self.w)
126 self.l is a QVBoxLayout. Add everything there.
127 self.w is a QWidget. Use it as parent.
130 def __init__(self, *args, **kwargs):
131 QDialog.__init__(self, *args, **kwargs)
133 # This freaking thing is hard
134 # It needs two layouts, one extra widget, the fingerscrollable
135 # property set to true *and* setWidgetResizable(True)
136 self._mm_l0=QVBoxLayout(self)
138 self._mm_q=QScrollArea(self)
139 self._mm_q.setWidgetResizable(True)
140 self._mm_q.setProperty('FingerScrollable', True)
142 self.w=QWidget(self._mm_q)
144 self.l=QVBoxLayout(self.w)
145 self._mm_q.setWidget(self.w)
146 self._mm_l0.addWidget(self._mm_q)
148 class AboutDialog(MyMsgDialog):
149 def __init__(self, *args, **kwargs):
150 MyMsgDialog.__init__(self, *args, **kwargs)
155 text-decoration: underline;
159 <p> A program to monitor the women's cycle. Good for planning (or acting ;-).
160 Inspired by "MyGirls" app which is (was?) available for Java ME capable phones.
162 <p style="color: orange;">
163 WARNING!!! This app is not guaranteed to be accurate or correct! You cannot
164 trust this program, or any program, to give accurate predictions!
165 The whole women-cycle-thing highly depends on a number of factors that
166 only a doctor can tell.
168 <div class="title">Copyright</div>
169 <p> Copyright © 2010, Stefanos Harhalakis <v13@v13.gr>
171 <p> Send comments and bug reports to the above address.
173 <div class="title">License</div>
174 <p> This program is free software: you can redistribute it and/or modify
175 it under the terms of the GNU General Public License as published by
176 the Free Software Foundation, either version 3 of the License, or
177 (at your option) any later version.
178 <p> This program is distributed in the hope that it will be useful,
179 but WITHOUT ANY WARRANTY; without even the implied warranty of
180 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
181 GNU General Public License for more details.
182 <p> You should have received a copy of the GNU General Public License
183 along with this program. If not, see
184 <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
189 self.setWindowTitle(self.tr("About MaeGirls"))
191 self.ltitle=QLabel("MaeGirls v" + config.version, self.w)
192 self.ltitle.setObjectName("title")
193 self.ltitle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
194 self.ltitle.setAlignment(Qt.AlignCenter)
195 self.l.addWidget(self.ltitle)
197 self.label=QLabel(txt, self.w)
198 self.label.setWordWrap(True)
199 self.label.setTextFormat(Qt.RichText)
200 self.label.setAlignment(Qt.AlignJustify)
201 self.l.addWidget(self.label)
203 self.ltitle.setStyleSheet("""
206 color: rgb(192,192,192);
207 margin-bottom: 0.5ex;
211 class HelpDialog(MyMsgDialog):
212 def __init__(self, *args, **kwargs):
213 MyMsgDialog.__init__(self, *args, **kwargs)
216 <p> MaeGirls shows information about women's cycle using some generic
217 guidelines: By default it assumes that ovulation happens 14 days before the
218 start of the next period and that the period cycle is constant. Also, it
219 assumes that sperm can live for 4 days, while an egg can live for 2 days.
221 <p> Using the configure menu you can change the duration of the cycle,
222 the duration of the menstruation and the day the ovulation happens. When
223 specifying the ovulation you can use negative numbers to indicate the number
224 of days before the end of the period, or positive numbers to indicate the
225 number of days after the beginning of the period. Default value for ovulation
228 <p> MaeGirls supports tracking of multiple women. While there have been a lot
229 of negative comments for this option, I find it useful even for women.
230 Please don't complain or give negative feedback for an option that you're not
233 <p style="color: orange;">
234 WARNING!!! The prediction is not always correct. There are FAR TOO MANY
235 exceptions to the above rules!!! You MUST consult a doctor in order to get
236 accurate predictions!!! Use the program at your own risk!!!
238 <p> Assuming that you understand the risks of blindly trusting this program,
239 you become entitled to read the graph as follows:
240 <p> <span style="color: red">In red:</span> The days that menstruation
241 happens. Default: 5 days.
242 <p> <span style="color: green">In green:</span> The fertile days as described above.
243 <p> <span style="color: blue">In blue:</span> The days of PMS
244 (Premenstrual Syndrome), assumed to last 7 days.
246 <p> Navigation is easy: Use left-right finger movement to move the calendar
247 view. Use up-down finger movement to zoom in/out.
250 self.setWindowTitle(self.tr("Help"))
252 self.label=QLabel(txt, self.w)
253 self.label.setWordWrap(True)
254 self.label.setTextFormat(Qt.RichText)
255 self.label.setAlignment(Qt.AlignJustify)
256 self.l.addWidget(self.label)
258 class GirlsDialog(QDialog):
259 def __init__(self, *args, **kwargs):
260 QDialog.__init__(self, *args, **kwargs)
262 self.l0=QHBoxLayout(self)
264 self.lstm=QStringListModel()
265 self.lst=QListView(self)
266 self.lst.setModel(self.lstm)
268 self.lst.setProperty("FingerScrollable", True)
270 self.l0.addWidget(self.lst)
272 self.buttonNew=QPushButton(self)
273 self.buttonNew.setText(self.tr("New"))
275 self.buttonSelect=QPushButton(self)
276 self.buttonSelect.setText(self.tr("Select"))
278 self.buttonDelete=QPushButton(self)
279 self.buttonDelete.setText(self.tr("Delete"))
281 spacer=QSpacerItem(20, 20, QSizePolicy.Minimum,QSizePolicy.Expanding)
283 self.l1=QVBoxLayout()
284 self.l0.addLayout(self.l1)
285 self.l1.addWidget(self.buttonNew)
286 self.l1.addWidget(self.buttonSelect)
287 self.l1.addWidget(self.buttonDelete)
288 self.l1.addItem(spacer)
290 self.buttonNew.clicked.connect(self.slotNew)
291 self.buttonDelete.clicked.connect(self.slotDelete)
292 self.buttonSelect.clicked.connect(self.slotSelect)
294 def _get_selection(self):
295 sel=self.lst.selectedIndexes()
298 ret=str(d.data().toString())
304 def exec_(self, current):
306 girls=config.loadGirls()
309 self.lstm.setStringList(dt)
314 # Set current selection
315 idx=dt.index(current)
317 # Either I'm doing something stupid, or this is a QT bug
318 # The selection works but isn't shown
319 idx2=self.lstm.index(idx, 0)
320 self.lst.setCurrentIndex(idx2)
321 # Give if focus to show the current selection - is this normal?
322 self.lst.setFocus(Qt.OtherFocusReason)
332 def slotDelete(self):
334 self.which=self._get_selection()
337 def slotSelect(self):
339 self.which=self._get_selection()
342 class MaeGirls(QMainWindow):
343 def __init__(self, algo):
344 QMainWindow.__init__(self)
347 self.setAttribute(Qt.WA_Maemo5AutoOrientation, True)
353 # self.dlgConfig=ConfigDialog(self)
354 # self.dlgAbout=AboutDialog(self)
355 # self.dlgHelp=HelpDialog(self)
363 def setupUi(self, algo):
364 self.centralwidget=QWidget(self)
365 self.setCentralWidget(self.centralwidget)
367 self.l0=QVBoxLayout(self.centralwidget)
369 self.dg=DaysGraph(algo, self.centralwidget)
370 self.l0.addWidget(self.dg)
373 self.menuconfig=QAction(self.tr('Configure'), self)
374 self.menuconfig.triggered.connect(self.menuConfig)
376 self.menureset=QAction(self.tr('Go to today'), self)
377 self.menureset.triggered.connect(self.menuReset)
379 self.menugirls=QAction(self.tr('Girls'), self)
380 self.menugirls.triggered.connect(self.menuGirls)
382 self.menuabout=QAction(self.tr('About'), self)
383 self.menuabout.triggered.connect(self.menuAbout)
385 self.menuhelp=QAction(self.tr('Help'), self)
386 self.menuhelp.triggered.connect(self.menuHelp)
389 m.addAction(self.menureset)
390 m.addAction(self.menuconfig)
391 m.addAction(self.menugirls)
392 m.addAction(self.menuhelp)
393 m.addAction(self.menuabout)
395 def updateTitle(self):
396 txt="MaeGirls - %s" % (self.girl, )
397 self.setWindowTitle(txt)
399 def setAlgo(self, algo):
400 self.dg.setAlgo(algo)
402 def setGirl(self, name):
403 cfg=config.loadGirl(name)
405 #self.algo.setReference(cfg['day0'], cfg['cycle'])
406 self.algo.setReference(cfg['day0'], cfg)
410 def menuConfig(self):
411 if self.dlgConfig==None:
412 self.dlgConfig=ConfigDialog(self)
416 'cycle': self.algo.cycleLength(),
417 'day0': self.algo.currentDayInCycle(),
418 'menstr': self.algo.menstruationDuration(),
419 'ovday': self.algo.ovulationDay()
422 self.dlgConfig.initValues(dt)
424 ret=self.dlgConfig.exec_()
426 if ret==self.dlgConfig.Accepted:
429 name=self.dlgConfig.name
430 day0=today-self.dlgConfig.current
433 'cycle': self.dlgConfig.cycle,
435 'ovday': self.dlgConfig.ovday,
436 'menstr': self.dlgConfig.menstr,
439 config.storeGirl(name, dt)
440 config.setCurrentGirl(name)
442 # If this is a rename, remove the old one
444 config.removeGirl(self.girl)
451 if self.dlgGirls==None:
452 self.dlgGirls=GirlsDialog(self)
454 ret=self.dlgGirls.exec_(self.girl)
456 what=self.dlgGirls.what
457 which=self.dlgGirls.which
459 # Determine a unique name
463 while config.girlExists(name):
465 name="%s%d" % (base, idx)
469 config.setCurrentGirl(name)
473 elif what=='delete' and which!=None:
475 msg=QMessageBox(self)
476 msg.setText(self.tr('You cannot delete the current girl'))
479 config.removeGirl(which)
480 elif what=='select' and which!=None:
481 config.setCurrentGirl(which)
485 if self.dlgAbout==None:
486 self.dlgAbout=AboutDialog(self)
488 ret=self.dlgAbout.exec_()
491 if self.dlgHelp==None:
492 self.dlgHelp=HelpDialog(self)
494 ret=self.dlgHelp.exec_()
504 # Create the application
505 app=QApplication(sys.argv)
507 # This returns the country and *NOT* the required locale
508 # This means that if you have'set language==en_GB and countr==Greece
509 # it will return el_GR.
510 # IOW: If you want english messages this will not work
511 #loc=QLocale.system().name()
513 loc=locale.setlocale(locale.LC_MESSAGES, '')
517 qttr.load("qt_" + loc,
518 QLibraryInfo.location(QLibraryInfo.TranslationsPath))
519 app.installTranslator(qttr)
522 maetr.load("maegirls_" + loc,
523 "/usr/share/maegirls/translations")
525 # Install the translation
526 app.installTranslator(maetr)
528 # One day support portrait mode
529 #app.setAttribute(Qt.WA_Maemo5PortraitOrientation, True);
531 # Create the main window
547 # vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent: