├── share ├── oroboros.png └── oroboros.desktop ├── oroboros ├── gui │ ├── icons │ │ ├── add.png │ │ ├── edit-1.png │ │ ├── edit-2.png │ │ ├── find-1.png │ │ ├── find-2.png │ │ ├── image.png │ │ ├── open-1.png │ │ ├── open-2.png │ │ ├── save-1.png │ │ ├── save-2.png │ │ ├── skif.png │ │ ├── close-1.png │ │ ├── close-2.png │ │ ├── edit-all.png │ │ ├── gtk-edit.png │ │ ├── gtk-home.png │ │ ├── gtk-open.png │ │ ├── gtk-quit.png │ │ ├── multiply.png │ │ ├── new-all.png │ │ ├── open-all.png │ │ ├── oroboros.png │ │ ├── transit.png │ │ ├── astrolog32.png │ │ ├── close-all.png │ │ ├── composite.png │ │ ├── direction.png │ │ ├── earth-icon.png │ │ ├── gtk-about.png │ │ ├── gtk-execute.png │ │ ├── gtk-refresh.png │ │ ├── planets │ │ │ ├── asc.png │ │ │ ├── dsc.png │ │ │ ├── ic.png │ │ │ ├── mc.png │ │ │ ├── sun.png │ │ │ ├── ceres.png │ │ │ ├── earth.png │ │ │ ├── eris.png │ │ │ ├── juno.png │ │ │ ├── mars.png │ │ │ ├── moon.png │ │ │ ├── pluto.png │ │ │ ├── sedna.png │ │ │ ├── venus.png │ │ │ ├── vesta.png │ │ │ ├── zeus.png │ │ │ ├── asteroid.png │ │ │ ├── chiron.png │ │ │ ├── cupido.png │ │ │ ├── eastpt.png │ │ │ ├── jupiter.png │ │ │ ├── kronos.png │ │ │ ├── lilith.png │ │ │ ├── mercury.png │ │ │ ├── neptune.png │ │ │ ├── nessus.png │ │ │ ├── pallas.png │ │ │ ├── pholus.png │ │ │ ├── poseidon.png │ │ │ ├── priapus.png │ │ │ ├── quaoar.png │ │ │ ├── saturn.png │ │ │ ├── uranus.png │ │ │ ├── uranus_a.png │ │ │ ├── varuna.png │ │ │ ├── vertex.png │ │ │ ├── vulkanus.png │ │ │ ├── fixedstar.png │ │ │ ├── part_fortune.png │ │ │ ├── pluto_lowell.png │ │ │ ├── north_lunar_node.png │ │ │ └── south_lunar_node.png │ │ ├── progression.png │ │ ├── save-as-1.png │ │ ├── save-as-2.png │ │ ├── cusps │ │ │ ├── cusp_01.png │ │ │ ├── cusp_02.png │ │ │ ├── cusp_03.png │ │ │ ├── cusp_04.png │ │ │ ├── cusp_05.png │ │ │ ├── cusp_06.png │ │ │ ├── cusp_07.png │ │ │ ├── cusp_08.png │ │ │ ├── cusp_09.png │ │ │ ├── cusp_10.png │ │ │ ├── cusp_11.png │ │ │ ├── cusp_12.png │ │ │ ├── sector_01.png │ │ │ ├── sector_02.png │ │ │ ├── sector_03.png │ │ │ ├── sector_04.png │ │ │ ├── sector_05.png │ │ │ ├── sector_06.png │ │ │ ├── sector_07.png │ │ │ ├── sector_08.png │ │ │ ├── sector_09.png │ │ │ ├── sector_10.png │ │ │ ├── sector_11.png │ │ │ ├── sector_12.png │ │ │ ├── sector_13.png │ │ │ ├── sector_14.png │ │ │ ├── sector_15.png │ │ │ ├── sector_16.png │ │ │ ├── sector_17.png │ │ │ ├── sector_18.png │ │ │ ├── sector_19.png │ │ │ ├── sector_20.png │ │ │ ├── sector_21.png │ │ │ ├── sector_22.png │ │ │ ├── sector_23.png │ │ │ ├── sector_24.png │ │ │ ├── sector_25.png │ │ │ ├── sector_26.png │ │ │ ├── sector_27.png │ │ │ ├── sector_28.png │ │ │ ├── sector_29.png │ │ │ ├── sector_30.png │ │ │ ├── sector_31.png │ │ │ ├── sector_32.png │ │ │ ├── sector_33.png │ │ │ ├── sector_34.png │ │ │ ├── sector_35.png │ │ │ └── sector_36.png │ │ ├── gtk-directory.png │ │ ├── midspacetime.png │ │ ├── properties-1.png │ │ ├── properties-2.png │ │ ├── gtk-preferences.png │ │ └── zodiac │ │ │ ├── sign_01.png │ │ │ ├── sign_02.png │ │ │ ├── sign_03.png │ │ │ ├── sign_04.png │ │ │ ├── sign_05.png │ │ │ ├── sign_06.png │ │ │ ├── sign_07.png │ │ │ ├── sign_08.png │ │ │ ├── sign_09.png │ │ │ ├── sign_10.png │ │ │ ├── sign_10a.png │ │ │ ├── sign_11.png │ │ │ └── sign_12.png │ ├── __init__.py │ ├── translations.py │ ├── orbmodifierwidget.py │ ├── chtimage.py │ ├── toolbar.py │ ├── centralwidget.py │ ├── newtabdialog.py │ ├── orbfiltersmandialog.py │ ├── aspfiltersmandialog.py │ ├── plntfiltersmandialog.py │ ├── midpfiltersmandialog.py │ ├── orbrestrmandialog.py │ ├── profectiondialog.py │ ├── asprestrmandialog.py │ ├── harmonicsdialog.py │ ├── menubar.py │ ├── chtflowers.py │ ├── filtersmandialog.py │ ├── saveimagedialog.py │ ├── coordswidget.py │ ├── app.py │ ├── geonames.py │ ├── aspfilterdialog.py │ ├── orbfilterdialog.py │ ├── names.py │ ├── dockwidget.py │ └── chtpainter.py ├── core │ ├── __init__.py │ ├── etc.txt │ ├── desktop.py │ ├── parts.py │ ├── multicharts.py │ ├── geonames.py │ ├── colors.py │ ├── astrolog32.py │ ├── orbs.py │ ├── kwdict.py │ ├── skylendar.py │ ├── timezone.py │ ├── charts.py │ ├── hgrepo.py │ ├── cfg.py │ ├── etc.py │ ├── db.py │ └── aspectsresults.py ├── cli │ ├── __init__.py │ ├── main.py │ └── hgrepo.py ├── irc │ ├── README.txt │ ├── test.py │ ├── config.py │ ├── __init__.py │ └── plugin.py └── __init__.py ├── README.TXT ├── bin ├── oroboros └── oroboros-hg ├── .gitignore ├── CREDITS.txt ├── TODO.txt ├── docutils.cfg ├── epydoc.cfg ├── MANIFEST.in ├── BUGS.txt ├── Makefile ├── INSTALL.txt ├── README.txt.old └── setup.py /share/oroboros.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/share/oroboros.png -------------------------------------------------------------------------------- /oroboros/gui/icons/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/add.png -------------------------------------------------------------------------------- /oroboros/gui/icons/edit-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/edit-1.png -------------------------------------------------------------------------------- /oroboros/gui/icons/edit-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/edit-2.png -------------------------------------------------------------------------------- /oroboros/gui/icons/find-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/find-1.png -------------------------------------------------------------------------------- /oroboros/gui/icons/find-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/find-2.png -------------------------------------------------------------------------------- /oroboros/gui/icons/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/image.png -------------------------------------------------------------------------------- /oroboros/gui/icons/open-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/open-1.png -------------------------------------------------------------------------------- /oroboros/gui/icons/open-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/open-2.png -------------------------------------------------------------------------------- /oroboros/gui/icons/save-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/save-1.png -------------------------------------------------------------------------------- /oroboros/gui/icons/save-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/save-2.png -------------------------------------------------------------------------------- /oroboros/gui/icons/skif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/skif.png -------------------------------------------------------------------------------- /oroboros/gui/icons/close-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/close-1.png -------------------------------------------------------------------------------- /oroboros/gui/icons/close-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/close-2.png -------------------------------------------------------------------------------- /oroboros/gui/icons/edit-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/edit-all.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-edit.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-home.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-open.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-quit.png -------------------------------------------------------------------------------- /oroboros/gui/icons/multiply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/multiply.png -------------------------------------------------------------------------------- /oroboros/gui/icons/new-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/new-all.png -------------------------------------------------------------------------------- /oroboros/gui/icons/open-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/open-all.png -------------------------------------------------------------------------------- /oroboros/gui/icons/oroboros.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/oroboros.png -------------------------------------------------------------------------------- /oroboros/gui/icons/transit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/transit.png -------------------------------------------------------------------------------- /oroboros/gui/icons/astrolog32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/astrolog32.png -------------------------------------------------------------------------------- /oroboros/gui/icons/close-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/close-all.png -------------------------------------------------------------------------------- /oroboros/gui/icons/composite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/composite.png -------------------------------------------------------------------------------- /oroboros/gui/icons/direction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/direction.png -------------------------------------------------------------------------------- /oroboros/gui/icons/earth-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/earth-icon.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-about.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-execute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-execute.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-refresh.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/asc.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/dsc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/dsc.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/ic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/ic.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/mc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/mc.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/sun.png -------------------------------------------------------------------------------- /oroboros/gui/icons/progression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/progression.png -------------------------------------------------------------------------------- /oroboros/gui/icons/save-as-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/save-as-1.png -------------------------------------------------------------------------------- /oroboros/gui/icons/save-as-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/save-as-2.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_01.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_02.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_03.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_04.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_05.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_06.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_07.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_08.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_09.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_10.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_11.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/cusp_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/cusp_12.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-directory.png -------------------------------------------------------------------------------- /oroboros/gui/icons/midspacetime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/midspacetime.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/ceres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/ceres.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/earth.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/eris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/eris.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/juno.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/juno.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/mars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/mars.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/moon.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/pluto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/pluto.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/sedna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/sedna.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/venus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/venus.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/vesta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/vesta.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/zeus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/zeus.png -------------------------------------------------------------------------------- /oroboros/gui/icons/properties-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/properties-1.png -------------------------------------------------------------------------------- /oroboros/gui/icons/properties-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/properties-2.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_01.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_02.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_03.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_04.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_05.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_06.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_07.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_08.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_09.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_10.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_11.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_12.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_13.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_14.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_15.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_16.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_17.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_18.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_19.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_20.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_21.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_22.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_23.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_24.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_25.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_26.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_27.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_28.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_29.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_30.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_31.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_32.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_33.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_34.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_35.png -------------------------------------------------------------------------------- /oroboros/gui/icons/cusps/sector_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/cusps/sector_36.png -------------------------------------------------------------------------------- /oroboros/gui/icons/gtk-preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/gtk-preferences.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/asteroid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/asteroid.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/chiron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/chiron.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/cupido.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/cupido.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/eastpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/eastpt.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/jupiter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/jupiter.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/kronos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/kronos.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/lilith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/lilith.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/mercury.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/mercury.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/neptune.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/neptune.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/nessus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/nessus.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/pallas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/pallas.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/pholus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/pholus.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/poseidon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/poseidon.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/priapus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/priapus.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/quaoar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/quaoar.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/saturn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/saturn.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/uranus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/uranus.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/uranus_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/uranus_a.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/varuna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/varuna.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/vertex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/vertex.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/vulkanus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/vulkanus.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_01.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_02.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_03.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_04.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_05.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_06.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_07.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_08.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_09.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_10.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_10a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_10a.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_11.png -------------------------------------------------------------------------------- /oroboros/gui/icons/zodiac/sign_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/zodiac/sign_12.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/fixedstar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/fixedstar.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/part_fortune.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/part_fortune.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/pluto_lowell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/pluto_lowell.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/north_lunar_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/north_lunar_node.png -------------------------------------------------------------------------------- /oroboros/gui/icons/planets/south_lunar_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrorigin/oroboros/HEAD/oroboros/gui/icons/planets/south_lunar_node.png -------------------------------------------------------------------------------- /oroboros/core/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Oroboros core package. 6 | 7 | """ 8 | 9 | # End. 10 | -------------------------------------------------------------------------------- /oroboros/cli/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Oroboros command-line package. 6 | 7 | """ 8 | 9 | # End. 10 | -------------------------------------------------------------------------------- /oroboros/gui/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Oroboros user interface (PyQt4). 6 | 7 | """ 8 | 9 | # End. 10 | -------------------------------------------------------------------------------- /README.TXT: -------------------------------------------------------------------------------- 1 | =========================== 2 | Oroboros Astrology Software 3 | =========================== 4 | 5 | - https://pypi.python.org/pypi/oroboros 6 | 7 | .. 8 | -------------------------------------------------------------------------------- /bin/oroboros: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Oroboros main script. 6 | 7 | """ 8 | 9 | import oroboros.cli.main 10 | 11 | # End. 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Oroboros gitignore 2 | *~ 3 | *.bak 4 | *.html 5 | *.orig 6 | *.pyc 7 | build* 8 | dist* 9 | doc 10 | MANIFEST 11 | 12 | # erik4 13 | *.e4p 14 | *.e4q 15 | *.e4t 16 | 17 | -------------------------------------------------------------------------------- /CREDITS.txt: -------------------------------------------------------------------------------- 1 | ======================= 2 | Oroboros - Credits file 3 | ======================= 4 | 5 | - Pholus glyph inspired by 6 | `Z.B.Stein website `_ 7 | 8 | .. 9 | -------------------------------------------------------------------------------- /bin/oroboros-hg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Oroboros-Mercurial command-line utilities. 6 | 7 | """ 8 | 9 | import oroboros.cli.hgrepo 10 | 11 | # End. 12 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | ==================== 2 | Oroboros - Todo file 3 | ==================== 4 | 5 | Or: things that could be done, and that nobody does. 6 | 7 | - Missing (and better) glyphs 8 | - Translations 9 | - A win32 port (including pyswisseph) 10 | - Debugging... 11 | 12 | .. 13 | -------------------------------------------------------------------------------- /oroboros/irc/README.txt: -------------------------------------------------------------------------------- 1 | ======================= 2 | Oroboros Supybot plugin 3 | ======================= 4 | 5 | Provides an IRC interface to Oroboros astrology software. 6 | 7 | Copy this directory in your plugins directory (as 'Oroboros') or just make 8 | a symbolic link to it. 9 | 10 | .. 11 | -------------------------------------------------------------------------------- /docutils.cfg: -------------------------------------------------------------------------------- 1 | # Oroboros - docutils 2 | 3 | [general] 4 | 5 | title = Oroboros 6 | generator = on 7 | recurse = off 8 | source-link = on 9 | 10 | [html4css1 writer] 11 | 12 | link-stylesheet = yes 13 | stylesheet = http://www.atarax.org/style.css 14 | embed-stylesheet = no 15 | cloak_email_adresses = on 16 | -------------------------------------------------------------------------------- /oroboros/irc/test.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Copyright (c) 2008, Stanislas Marquis 3 | # All rights reserved. 4 | # 5 | # 6 | ### 7 | 8 | from supybot.test import * 9 | 10 | class OroborosTestCase(PluginTestCase): 11 | plugins = ('Oroboros',) 12 | 13 | 14 | # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: 15 | -------------------------------------------------------------------------------- /oroboros/core/etc.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Oroboros configuration file 3 | # ___________________________ 4 | # 5 | # Lines beginning with "#" or ";" are ignored 6 | # and may be used to provide comments. 7 | # 8 | 9 | [sqlite] 10 | 11 | # Path to database file 12 | # 13 | path = ~/.oroboros/oroboros.db 14 | 15 | # End. 16 | -------------------------------------------------------------------------------- /share/oroboros.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Type=Application 4 | Name=Oroboros 5 | GenericName=Astrology software 6 | Comment=Astrology software written in Python 7 | Icon=oroboros.png 8 | Exec=oroboros 9 | TryExec=oroboros 10 | Terminal=false 11 | Type=Application 12 | StartupNotify=true 13 | Categories=Science;Astronomy; 14 | -------------------------------------------------------------------------------- /epydoc.cfg: -------------------------------------------------------------------------------- 1 | # Oroboros - Epydoc 2 | 3 | [epydoc] 4 | 5 | name = Oroboros API doc 6 | url = http://oroboros.atarax.org 7 | 8 | modules = oroboros 9 | top = oroboros 10 | 11 | output = html 12 | css = grayscale 13 | target = doc 14 | docformat = restructuredtext 15 | sourcecode = no 16 | 17 | verbosity = 1 18 | private = no 19 | imports = no 20 | 21 | #. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include oroboros/*.txt 2 | include oroboros/core/*.sql 3 | include oroboros/core/*.txt 4 | include oroboros/gui/icons/*.png 5 | include oroboros/gui/icons/zodiac/*.png 6 | include oroboros/gui/icons/cusps/*.png 7 | include oroboros/gui/icons/planets/*.png 8 | include oroboros/gui/tr/*.qm 9 | include oroboros/gui/tr/*.ts 10 | include oroboros/irc/*.txt 11 | include share/* 12 | 13 | include *.txt 14 | include *.cfg 15 | include Makefile 16 | include Oroboros.e4* 17 | include MANIFEST.in 18 | -------------------------------------------------------------------------------- /BUGS.txt: -------------------------------------------------------------------------------- 1 | ========================== 2 | Oroboros - Known bugs file 3 | ========================== 4 | 5 | The fixed stars can randomly crash (segfault) the application. 6 | I suspect this is due to an unconsistent fixstars.cat file pointer 7 | management inside the swisseph itself. See chartcalc.py file. 8 | Workaround: close sqlite db before calls to fixstars. 9 | 10 | The geonames.org query system doesnt work with Qt version 4.4.0. 11 | At least for me. Everything ok with previous versions. 12 | 13 | I wish to get back some feedback about bugs. Even noobuntu users. 14 | Please send mail to stnsls@gmail.com. Thanks in advance. 15 | 16 | .. 17 | -------------------------------------------------------------------------------- /oroboros/gui/translations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Translations. 6 | 7 | """ 8 | 9 | import os.path 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | 15 | __all__ = ['languages', 'load'] 16 | 17 | 18 | # available translations (fr, en, ...) 19 | languages = [] 20 | 21 | 22 | _baseDir = os.path.join(os.path.dirname(os.path.abspath(__file__))) 23 | 24 | 25 | def load(lng, app): 26 | if lng not in languages: 27 | return 28 | ts = QTranslator(app) 29 | ts.load('%s.qm' % lng, os.path.join(_baseDir, 'tr')) 30 | app.installTranslator(ts) 31 | 32 | 33 | 34 | # End. 35 | -------------------------------------------------------------------------------- /oroboros/gui/orbmodifierwidget.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Orbs modifiers widgets. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | 13 | __all__ = ['OrbModifierSpinBox'] 14 | 15 | 16 | class OrbModifierSpinBox(QDoubleSpinBox): 17 | """Orb modifier spin box. 18 | 19 | For the moment only relative modifiers are supported (percent). 20 | 21 | """ 22 | 23 | def __init__(self, parent): 24 | QDoubleSpinBox.__init__(self, parent) 25 | self.setRange(-100, 100) 26 | self.setSuffix(self.tr('%', 'percent')) 27 | self.setButtonSymbols(QAbstractSpinBox.PlusMinus) 28 | 29 | 30 | 31 | # End. 32 | -------------------------------------------------------------------------------- /oroboros/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Astrology for Python. 6 | 7 | :See: `Homepage `_ 8 | :Author: Stanislas Marquis 9 | :Contact: stnsls@gmail.com 10 | :License: `GNU GPL v3 `_ 11 | 12 | Requirements: 13 | 14 | - `pyswisseph `_ 15 | - `pytz `_ 16 | - `PyQt4 `_ 17 | 18 | Optionally; 19 | 20 | - `docutils `_ 21 | - `Mercurial `_ 22 | - `Supybot `_ 23 | 24 | """ 25 | 26 | __version__ = '20080712' 27 | 28 | # End. 29 | -------------------------------------------------------------------------------- /oroboros/core/desktop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | """ 5 | Desktop manager. 6 | 7 | """ 8 | 9 | from oroboros.core.bicharts import BiChart 10 | 11 | 12 | __all__ = ['charts'] 13 | 14 | 15 | class OpenedCharts(list): 16 | """Opened charts object.""" 17 | 18 | def append(self, cht): 19 | if not isinstance(cht, BiChart): 20 | cht = BiChart(cht) 21 | list.append(self, cht) 22 | 23 | def insert(self, idx, cht): 24 | if not isinstance(cht, BiChart): 25 | cht = BiChart(cht) 26 | list.insert(self, idx, cht) 27 | 28 | def __setitem__(self, item): 29 | if not isinstance(item, BiChart): 30 | item = BiChart(item) 31 | list.__setitem__(self, item) 32 | 33 | 34 | #: Opened charts singleton 35 | charts = OpenedCharts() 36 | 37 | 38 | 39 | # End. 40 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # oroboros makefile 2 | 3 | RM = rm -rf 4 | PY = python 5 | CD = cd 6 | PYDOC = epydoc 7 | RST = buildhtml.py 8 | 9 | all: 10 | 11 | clean: 12 | $(RM) build dist MANIFEST 13 | 14 | cleanpy: 15 | $(RM) oroboros/*.pyc oroboros/core/*.pyc oroboros/gui/*.pyc oroboros/cli/*.pyc oroboros/irc/*.pyc 16 | 17 | cleandoc: 18 | $(RM) doc *.html 19 | 20 | cleanhome: 21 | $(RM) ~/.oroboros 22 | 23 | mrproper: clean cleanpy cleandoc cleanhome 24 | 25 | doc: 26 | $(PYDOC) --config epydoc.cfg 27 | 28 | rst: 29 | $(RST) --config docutils.cfg 30 | 31 | dist: mrproper doc rst 32 | $(PY) setup.py sdist --formats=bztar 33 | 34 | rpm: mrproper 35 | $(PY) setup.py bdist_rpm --use-bzip2 --group=Sciences/Astronomy 36 | 37 | upload: mrproper doc rst 38 | $(PY) setup.py sdist --formats=bztar upload 39 | 40 | install: 41 | $(PY) setup.py install 42 | 43 | # end. 44 | -------------------------------------------------------------------------------- /oroboros/irc/config.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Copyright (c) 2008, Stanislas Marquis 3 | # All rights reserved. 4 | # 5 | # 6 | ### 7 | 8 | import supybot.conf as conf 9 | import supybot.registry as registry 10 | 11 | def configure(advanced): 12 | # This will be called by supybot to configure this module. advanced is 13 | # a bool that specifies whether the user identified himself as an advanced 14 | # user or not. You should effect your configuration by manipulating the 15 | # registry as appropriate. 16 | from supybot.questions import expect, anything, something, yn 17 | conf.registerPlugin('Oroboros', True) 18 | 19 | 20 | Oroboros = conf.registerPlugin('Oroboros') 21 | # This is where your configuration variables (if any) should go. For example: 22 | # conf.registerGlobalValue(Oroboros, 'someConfigVariableName', 23 | # registry.Boolean(False, """Help for someConfigVariableName.""")) 24 | 25 | 26 | # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: 27 | -------------------------------------------------------------------------------- /INSTALL.txt: -------------------------------------------------------------------------------- 1 | ======================= 2 | Oroboros - Install file 3 | ======================= 4 | 5 | Requirements 6 | ------------ 7 | Most of them are pre-built and packaged for the major Linux distros. 8 | If not, they come with their own install instructions... 9 | 10 | - `Python `_ language, version >= 2.5 11 | 12 | Additional modules: 13 | 14 | - sqlite (included in Python >= 2.5) 15 | - `pytz `_ 16 | - `pyswisseph `_ version >= 1.74.00-0 17 | - `PyQt4 `_ version >= 4.3 18 | 19 | Optionally: 20 | 21 | - `docutils `_ latest snapshot 22 | - `Mercurial `_ 23 | - `Supybot `_ 24 | 25 | Install 26 | ------- 27 | For a global site install, run (as usual) the setup script with 28 | superuser permissions: ``python setup.py install`` 29 | 30 | Or use the Makefile, just type: ``make install`` 31 | 32 | .. 33 | -------------------------------------------------------------------------------- /oroboros/core/parts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Parts calculations. 6 | 7 | """ 8 | 9 | import swisseph as swe 10 | 11 | 12 | __all__ = ['calc_ut'] 13 | 14 | 15 | def calc_ut(part, cht): 16 | """Calculate part positions. 17 | 18 | Chart should have houses calculated (asc, mc, etc). 19 | 20 | :type part: str 21 | :type cht: Chart 22 | """ 23 | if part == 'Part of Fortune (Rudhyar)': 24 | return part_of_fortune_rudhyar(cht) 25 | raise ValueError('Invalid part %s.' % part) 26 | 27 | 28 | def part_of_fortune_rudhyar(cht): 29 | """Calculate part of fortune (Rudhyar). 30 | 31 | :type cht: Chart 32 | """ 33 | flag = cht._filter.get_calcflag() 34 | sun = swe.calc_ut(cht.julday, swe.SUN, flag) 35 | moon = swe.calc_ut(cht.julday, swe.MOON, flag) 36 | pos = swe.degnorm( 37 | cht._houses.get_positions('Asc')._longitude + ( 38 | swe.difdegn(moon[0], sun[0]))) 39 | return (pos, 0, 0, 0, 0, 0) 40 | 41 | 42 | 43 | def _test(): 44 | import doctest 45 | doctest.testmod() 46 | 47 | 48 | if __name__ == '__main__': 49 | _test() 50 | 51 | # End. 52 | -------------------------------------------------------------------------------- /oroboros/gui/chtimage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Produce charts images. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | from PyQt4.QtSvg import QSvgGenerator 12 | 13 | 14 | from oroboros.gui.chtpainter import ChartPainter 15 | 16 | 17 | __all__ = ['makeImage', 'makeSvg'] 18 | 19 | 20 | def makeImage(chart, path, ext, width, height, quality=-1): 21 | """Create an image file for a chart. 22 | 23 | :type chart: BiChart 24 | :type path: str 25 | :type ext: str 26 | :type width: int 27 | :type height: int 28 | :type quality: int 29 | """ 30 | im = QImage(width, height, QImage.Format_ARGB32_Premultiplied) 31 | ChartPainter(im, chart) 32 | if not path.endswith(ext): 33 | path = '%s.%s' % (path, ext) 34 | im.save(path, None, quality) 35 | 36 | 37 | def makeSvg(chart, path, width, height): 38 | """Create a Svg file. 39 | 40 | :type chart: BiChart 41 | :type path: str 42 | :type width: int 43 | :type height: int 44 | """ 45 | svg = QSvgGenerator() 46 | if not path.endswith('.svg'): 47 | path = '%s%s' % (path, '.svg') 48 | svg.setFileName(path) 49 | svg.setSize(QSize(width, height)) 50 | ##svg.setResolution(100) 51 | ChartPainter(svg, chart) 52 | 53 | 54 | # End. 55 | -------------------------------------------------------------------------------- /oroboros/cli/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Main script. 6 | 7 | """ 8 | 9 | import sys 10 | 11 | import oroboros 12 | 13 | 14 | if len(sys.argv) == 1: 15 | from oroboros.gui.mainwin import main 16 | main() 17 | 18 | if len(sys.argv) > 2: 19 | sys.exit('oroboros: too many arguments. Try "oroboros --help" instead.') 20 | 21 | 22 | 23 | disclaimer = """ 24 | Oroboros - Astrology software for Python (version %s). 25 | 26 | Copyright (C) 2008 Stanislas Marquis 27 | Homepage http://oroboros.atarax.org 28 | 29 | This is free software; see the license for copying conditions. There is NO 30 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 31 | """ % (oroboros.__version__,) 32 | 33 | 34 | help_text = """%s 35 | usage: oroboros [option] 36 | 37 | options: 38 | --version print version 39 | -h, --help print this text 40 | """ % (disclaimer,) 41 | 42 | 43 | version_text = """%s 44 | Running on %s with Python %s. 45 | """ % (disclaimer, sys.platform, sys.version) 46 | 47 | 48 | cmd = sys.argv[1] 49 | 50 | if cmd in ('-h', '--help'): 51 | sys.exit(help_text) 52 | elif cmd == '--version': 53 | sys.exit(version_text) 54 | else: 55 | print('Ha ha ha ...!') 56 | 57 | 58 | # End. 59 | -------------------------------------------------------------------------------- /oroboros/irc/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Provides an IRC interface to Oroboros astrology software (using Supybot). 6 | 7 | You can test this utility *live* on EFnet #astrology channel. 8 | 9 | """ 10 | 11 | import supybot 12 | import supybot.world as world 13 | 14 | # Use this for the version of this plugin. You may wish to put a CVS keyword 15 | # in here if you're keeping the plugin in CVS or some similar system. 16 | __version__ = "20080610" 17 | 18 | # XXX Replace this with an appropriate author or supybot.Author instance. 19 | __author__ = supybot.Author('Stanislas Marquis', 'stnsls', 'stnsls@gmail.com') 20 | 21 | # This is a dictionary mapping supybot.Author instances to lists of 22 | # contributions. 23 | __contributors__ = {} 24 | 25 | # This is a url where the most recent plugin package can be downloaded. 26 | __url__ = 'http://pypi.python.org/pypi/oroboros' 27 | 28 | import config 29 | import plugin 30 | reload(plugin) # In case we're being reloaded. 31 | # Add more reloads here if you add third-party modules and want them to be 32 | # reloaded when this plugin is reloaded. Don't forget to import them as well! 33 | 34 | 35 | if world.testing: 36 | import test 37 | 38 | Class = plugin.Class 39 | configure = config.configure 40 | 41 | # End. 42 | 43 | -------------------------------------------------------------------------------- /oroboros/core/multicharts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Arrangement of up to 4 calculated charts. 6 | 7 | """ 8 | 9 | 10 | 11 | __all__ = ['MultiChart'] 12 | 13 | 14 | 15 | class MultiChart(list): 16 | """Multiple charts.""" 17 | 18 | __slots__ = ( 19 | ## display 20 | '_switched') 21 | 22 | def _get_switched(self): 23 | return self._switched 24 | 25 | def _set_switched(self, boolean): 26 | self._switched = boolean 27 | 28 | switched = property(_get_switched, _set_switched, 29 | doc='Switched charts status.') 30 | 31 | def __init__(self, cht1=None, cht2=None): 32 | list.__init__(self) 33 | if cht1 != None: 34 | self.insert(0, cht1) 35 | if cht2 != None: 36 | self.insert(1, cht2) 37 | self._switched = False 38 | self.calc() 39 | 40 | def append(self, cht): 41 | if not isinstance(cht, Chart): 42 | cht = Chart(cht) 43 | list.append(self, cht) 44 | self.calc() 45 | 46 | def insert(self, idx, cht): 47 | if not isinstance(cht, Chart): 48 | cht = Chart(cht) 49 | list.insert(self, idx, cht) 50 | self.calc() 51 | 52 | def pop(self, idx): 53 | list.pop(self, idx) 54 | if len(self) != 2: 55 | self._switched = False 56 | 57 | def __setitem__(self, idx, cht): 58 | if not isinstance(cht, Chart): 59 | cht = Chart(cht) 60 | list.__setitem__(self, idx, cht) 61 | self.calc() 62 | 63 | def __delitem__(self, item): 64 | list.__delitem__(self, item) 65 | if len(self) != 2: 66 | self._switched = False 67 | 68 | 69 | 70 | 71 | 72 | 73 | # End. 74 | -------------------------------------------------------------------------------- /oroboros/cli/hgrepo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Oroboros-Mercurial utilities. 6 | 7 | For more complex operations, directly use the core module, or hg itself. 8 | Or submit a patch... 9 | 10 | """ 11 | 12 | import sys 13 | 14 | if len(sys.argv) == 1: 15 | sys.exit('oroboros-hg: no arguments. Try "oroboros-hg --help" instead.') 16 | 17 | from oroboros.core import cfg 18 | from oroboros.core import hgrepo 19 | 20 | help_text = """ 21 | usage: oroboros-hg option[=arg] 22 | 23 | options: 24 | --init[=path] init charts repo in charts dir, default %s 25 | --clone[=src] copy charts repo in charts dir, default %s 26 | --pull[=src] pull changes from repo to charts dir, default %s 27 | --push[=src] push changes from charts dir to repo, default %s 28 | -h, --help print this text 29 | 30 | see also: hg 31 | """ % (cfg.charts_dir, cfg.hg_repo, cfg.hg_repo, cfg.charts_dir) 32 | 33 | cmd = sys.argv[1] 34 | 35 | def getarg(opt): 36 | """Get option argument. 37 | 38 | :type opt: str 39 | :rtype: str or None 40 | """ 41 | try: 42 | cmd, arg = opt.split('=') 43 | except ValueError: 44 | arg = None 45 | return arg 46 | 47 | if cmd in ('-h', '--help'): 48 | sys.exit(help_text) 49 | elif cmd.startswith('--init'): 50 | arg = getarg(cmd) 51 | hgrepo.init(arg) 52 | elif cmd.startswith('--clone'): 53 | arg = getarg(cmd) 54 | hgrepo.clone(arg) 55 | elif cmd.startswith('--pull'): 56 | arg = getarg(cmd) 57 | hgrepo.pull(arg) 58 | elif cmd.startswith('--push'): 59 | arg = getarg(cmd) 60 | hgrepo.push(arg) 61 | else: 62 | sys.exit(help_text) 63 | 64 | 65 | # End. 66 | -------------------------------------------------------------------------------- /oroboros/gui/toolbar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | """ 5 | Tool bar. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | 13 | __all__ = ['createToolBar'] 14 | 15 | 16 | def createToolBar(self): 17 | """Create tool bar.""" 18 | tr = self.tr 19 | # file actions 20 | toolBar = self.addToolBar(tr('Toolbar')) 21 | toolBar.addAction(self.actionNewMultiChart) 22 | toolBar.addAction(self.actionCloseMultiChart) 23 | toolBar.addSeparator() 24 | # charts actions 25 | toolBar.addAction(self.actionOpenChart1) 26 | toolBar.addAction(self.actionHideChart1) 27 | toolBar.addAction(self.actionEditChart1) 28 | toolBar.addAction(self.actionFilterChart1) 29 | toolBar.addAction(self.actionSaveChart1) 30 | toolBar.addAction(self.actionCloseChart1) 31 | toolBar.addSeparator() 32 | toolBar.addAction(self.actionOpenChart2) 33 | toolBar.addAction(self.actionHideChart2) 34 | toolBar.addAction(self.actionEditChart2) 35 | toolBar.addAction(self.actionFilterChart2) 36 | toolBar.addAction(self.actionSaveChart2) 37 | toolBar.addAction(self.actionCloseChart2) 38 | toolBar.addSeparator() 39 | toolBar.addAction(self.actionSwitchCharts) 40 | toolBar.addSeparator() 41 | toolBar.addAction(self.actionTransitMode) 42 | toolBar.addAction(self.actionProgressionMode) 43 | toolBar.addAction(self.actionDirectionMode) 44 | ## toolBar.addSeparator() 45 | ## toolBar.addAction(self.actionMultiplyPos) 46 | ## toolBar.addAction(self.actionAddPos) 47 | ## toolBar.addSeparator() 48 | ## toolBar.addAction(self.actionComposite) 49 | ## toolBar.addAction(self.actionMidSpaceTime) 50 | # quit 51 | toolBar.addSeparator() 52 | toolBar.addAction(self.actionExit) 53 | 54 | 55 | # End. 56 | -------------------------------------------------------------------------------- /oroboros/irc/plugin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Oroboros IRC plugin. 6 | 7 | """ 8 | 9 | import supybot.utils as utils 10 | from supybot.commands import * 11 | import supybot.plugins as plugins 12 | import supybot.ircutils as ircutils 13 | import supybot.callbacks as callbacks 14 | 15 | from oroboros.core.bicharts import BiChart 16 | from oroboros.core.charts import Chart 17 | from oroboros.gui.chtimage import makeImage 18 | 19 | 20 | class Oroboros(callbacks.Plugin): 21 | """Provides IRC interface to Oroboros astrology software""" 22 | 23 | threaded = False 24 | 25 | def current(self, irc, msg, args): 26 | """takes no arguments 27 | 28 | Draw the current chart, put it in the local http server, 29 | and give the address. 30 | """ 31 | path = '/home/sm/public_html/irc/chart.png' # your public path here 32 | ext = 'png' 33 | width = 800 34 | height = 800 35 | cht = BiChart(Chart()) 36 | makeImage(cht, path, ext, width, height) 37 | irc.reply('Done, http://ranoraraku.atarax.org/~sm/irc/chart.png') # your httpd here 38 | current = wrap(current) 39 | 40 | def showme(self, irc, msg, args, pth): 41 | """ 42 | 43 | Draw your chart and show it. 44 | """ 45 | path = '/home/sm/public_html/irc/%s.png' % pth # your public path here 46 | ext = 'png' 47 | width = 800 48 | height = 800 49 | cht = BiChart(Chart('/home/sm/charts/%s.xml' % pth)) # your charts dir here 50 | makeImage(cht, path, ext, width, height) 51 | pth = pth.replace(' ', '%20') 52 | irc.reply('Done, http://ranoraraku.atarax.org/~sm/irc/%s.png' % pth) # your httpd here 53 | showme = wrap(showme, ['text']) 54 | 55 | 56 | 57 | Class = Oroboros 58 | 59 | 60 | # End. 61 | -------------------------------------------------------------------------------- /README.txt.old: -------------------------------------------------------------------------------- 1 | ====================== 2 | Oroboros - Readme file 3 | ====================== 4 | 5 | *Oroboros* is an astrology application for the Python language. 6 | 7 | It is based on the Swiss Ephemeris library created by `AstroDienst`_ for the 8 | planetary calculations, the `Olson time zone`_ database, the `GeoNames.org`_ 9 | atlas database, and the `Qt toolkit`_ as graphical interface. It is also 10 | using `reStructured Text`_ for charts comments, and `Mercurial`_ as 11 | mirroring system for charts directories. 12 | 13 | As you can see, it is still in early stage of development. 14 | 15 | .. _`AstroDienst`: http://www.astro.com 16 | .. _`Olson time zone`: http://www.twinsun.com/tz/tz-link.htm 17 | .. _`GeoNames.org`: http://www.geonames.org 18 | .. _`Qt toolkit`: http://trolltech.com 19 | .. _`reStructured Text`: http://docutils.sourceforge.net 20 | .. _`Mercurial`: http://www.selenic.com/mercurial 21 | 22 | Requirements 23 | ------------ 24 | 25 | - `Python `_ version >= 2.5 26 | 27 | External modules: 28 | 29 | - `pytz `_ 30 | - `pyswisseph `_ 31 | - `PyQt4 `_ 32 | 33 | Optionally: 34 | 35 | - `Docutils `_ 36 | - `Mercurial `_ 37 | - `Supybot `_ 38 | 39 | Links 40 | ----- 41 | 42 | - Homepage: http://oroboros.atarax.org 43 | - Download: http://pypi.python.org/pypi/oroboros 44 | 45 | Author(s) 46 | --------- 47 | Project Maintainer: `S.Marquis `_ 48 | 49 | Thanks 50 | ------ 51 | 52 | - Alois Treindl, Dieter Koch for the Swiss Ephemeris 53 | http://www.astro.com/swisseph 54 | - Christophe Gros 55 | http://skylendar.kde.org 56 | - All people who indirectly contributed by providing free components and 57 | softwares, esp. the Python team, the pytz authors, the GeoNames.org team, 58 | the people at TrollTech and Riverbank, the Eric4 IDE folks, the GIMP dudes, 59 | docutils and epydoc authors, Mercurial staff, and all those that I forget... 60 | - Special thanks to http://www.4URockNMetal.com 61 | 62 | .. 63 | -------------------------------------------------------------------------------- /oroboros/core/geonames.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pythno 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | GeoNames.org search system. 6 | 7 | """ 8 | 9 | import urllib 10 | 11 | from oroboros.core import xmlutils 12 | from oroboros.core import geocoords 13 | 14 | 15 | __all__ = ['search'] 16 | 17 | 18 | def query(url): 19 | """Query GeoNames.org webservice. 20 | 21 | :type url: str 22 | :rtype: file-like 23 | :raise ValueError: query failed 24 | """ 25 | try: 26 | f = urllib.urlopen(url) 27 | except: 28 | #raise 29 | raise ValueError('Unable to query geonames %s.' % url) 30 | return f 31 | 32 | 33 | def search(name): 34 | """Query GeoNames.org. 35 | 36 | Return a list of tuples (name, country, lat, lon, elev, timezone) 37 | 38 | :type name: str 39 | :rtype: list 40 | """ 41 | url = 'http://ws.geonames.org/search?q=%s&featureClass=P&style=FULL' 42 | f = query(url % urllib.quote(name)) 43 | xml = xmlutils.parse(f) 44 | try: 45 | f.close() 46 | except: 47 | pass 48 | ret = list() 49 | all = xml.get_iterator('geoname') 50 | for a in all: 51 | lat = geocoords.Latitude() 52 | lon = geocoords.Longitude() 53 | dlat = float(a.get_child_text(tag='lat')) 54 | dlon = float(a.get_child_text(tag='lng')) 55 | lat.from_decimal(dlat) 56 | lon.from_decimal(dlon) 57 | elev = get_elevation(dlat, dlon) 58 | ret.append((a.get_child_text(tag='name'), 59 | a.get_child_text(tag='countryName'), 60 | lat, lon, elev, 61 | a.get_child_text(tag='timezone'))) 62 | return ret 63 | 64 | 65 | def get_elevation(lat, lon): 66 | """Get elevation (altitude) for a place. 67 | 68 | :type lat: numeric 69 | :type lon: numeric 70 | :rtype: Altitude 71 | """ 72 | if lat > 60 or lat < -66: # use gtopo30 73 | url = 'http://ws.geonames.org/gtopo30?lat=%s&lng=%s' 74 | else: # use srtm3 75 | url = 'http://ws.geonames.org/srtm3?lat=%s&lng=%s' 76 | f = query(url % (lat, lon)) 77 | elev = int(f.read().strip()) 78 | try: 79 | f.close() 80 | except: 81 | pass 82 | if elev < 0: 83 | elev = 0 84 | return geocoords.Altitude(elev) 85 | 86 | 87 | 88 | def _test(): 89 | import doctest 90 | doctest.testmod() 91 | 92 | 93 | if __name__ == '__main__': 94 | _test() 95 | 96 | # End. 97 | -------------------------------------------------------------------------------- /oroboros/gui/centralwidget.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Chart wheels. 6 | 7 | """ 8 | 9 | import os.path 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | from oroboros.gui import app 15 | from oroboros.gui.chtpainter import ChartPainter 16 | 17 | 18 | __all__ = ['CentralWidget'] 19 | 20 | 21 | _iconsDir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'icons') 22 | 23 | 24 | ## oroboros.ui.centralwidget.CentralWidget 25 | class CentralWidget(QTabWidget): 26 | 27 | def __init__(self, parent): 28 | QTabWidget.__init__(self, parent) 29 | self.connect(self, SIGNAL('currentChanged(int)'), 30 | parent.centralTabChangedEvent) 31 | 32 | def addTab(self, idx): 33 | """Create a new chart tab.""" 34 | tab = ChartWheelWidget(idx) 35 | QTabWidget.addTab(self, tab, app.desktop.charts[idx][0]._name) 36 | 37 | def tabRemoved(self, idx): 38 | for i in range(len(app.desktop.charts)): 39 | self.widget(i).resetIdx(i) 40 | app.mainwin.resetActions() 41 | 42 | def tabInserted(self, idx): 43 | app.mainwin.resetActions() 44 | 45 | def resetTab(self, idx): 46 | """Reload chart tab.""" 47 | self.setTabText(idx, app.desktop.charts[idx][0]._name) 48 | self.widget(idx).reset() 49 | 50 | def paintEvent(self, event): 51 | if len(app.desktop.charts) == 0: 52 | self.drawOroboros() 53 | 54 | def drawOroboros(self): 55 | """Paint the Oroboros on empty window.""" 56 | painter = QPainter(self) 57 | im = QImage(os.path.join(_iconsDir, 'oroboros.png')) 58 | target = QRect( 59 | (self.width()/2.0)-170, (self.height()/2.0)-170, 340, 340) 60 | painter.drawImage(target, im, QRect(0, 0, 340, 340)) 61 | 62 | 63 | 64 | ## oroboros.ui.centralwidget.ChartWheelWidget 65 | class ChartWheelWidget(QWidget): 66 | 67 | def __init__(self, idx): 68 | QWidget.__init__(self) 69 | self._idx = idx 70 | 71 | def paintEvent(self, event): 72 | """Draw chart on widget.""" 73 | painter = ChartPainter(self, app.desktop.charts[self._idx]) 74 | 75 | def reset(self): 76 | """Force repaint chart.""" 77 | self.update() 78 | 79 | def resetIdx(self, idx): 80 | """Modify chart idx.""" 81 | self._idx = idx 82 | 83 | 84 | 85 | # End. 86 | -------------------------------------------------------------------------------- /oroboros/gui/newtabdialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | New tab dialog. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | 13 | __all__ = ['NewTabDialog'] 14 | 15 | 16 | 17 | class NewTabDialog(QDialog): 18 | 19 | def __init__(self, parent=None): 20 | QDialog.__init__(self, parent) 21 | tr = self.tr 22 | self.setWindowTitle(tr('New Chart')) 23 | self.setSizeGripEnabled(True) 24 | # layout 25 | layout = QVBoxLayout(self) 26 | self.setLayout(layout) 27 | # button group 28 | self.grpButtons = QButtonGroup(self) 29 | # here-now button 30 | hereNowButton = QRadioButton(tr('Here-Now Chart'), self) 31 | hereNowButton.setChecked(True) 32 | self.grpButtons.addButton(hereNowButton, 0) 33 | layout.addWidget(hereNowButton) 34 | # open chart 35 | openButton = QRadioButton(tr('Open File'), self) 36 | self.grpButtons.addButton(openButton, 1) 37 | layout.addWidget(openButton) 38 | # custom chart 39 | customButton = QRadioButton(tr('Custom Chart'), self) 40 | self.grpButtons.addButton(customButton, 2) 41 | layout.addWidget(customButton) 42 | # open astrolog32 43 | openA32Button = QRadioButton(tr('Open Astrolog32'), self) 44 | self.grpButtons.addButton(openA32Button, 3) 45 | layout.addWidget(openA32Button) 46 | # open skif 47 | openSkifButton = QRadioButton(tr('Open Skif'), self) 48 | self.grpButtons.addButton(openSkifButton, 4) 49 | layout.addWidget(openSkifButton) 50 | # dialog buttons 51 | bLayout = QHBoxLayout() 52 | layout.addLayout(bLayout) 53 | cancelButton = QPushButton(tr('Cancel'), self) 54 | self.connect(cancelButton, SIGNAL('clicked()'), self.reject) 55 | bLayout.addWidget(cancelButton) 56 | okButton = QPushButton(tr('OK'), self) 57 | okButton.setDefault(True) 58 | self.connect(okButton, SIGNAL('clicked()'), self.accept) 59 | bLayout.addWidget(okButton) 60 | 61 | def exec_(self): 62 | ok = QDialog.exec_(self) 63 | if ok: 64 | return ok, self.grpButtons.checkedId() 65 | else: 66 | return ok, None 67 | 68 | 69 | 70 | def main(): 71 | import sys 72 | app = QApplication(sys.argv) 73 | main = NewTabDialog() 74 | main.show() 75 | sys.exit(app.exec_()) 76 | 77 | 78 | if __name__ == '__main__': 79 | main() 80 | 81 | # End. 82 | -------------------------------------------------------------------------------- /oroboros/core/colors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Colors. 6 | 7 | Create a color instance. 8 | 9 | >>> clr = RGBColor(120, 120) 10 | >>> clr.set(blue=30) 11 | >>> print clr 12 | 120,120,30 13 | 14 | """ 15 | 16 | 17 | __all__ = ['RGBColor'] 18 | 19 | 20 | class RGBColor(object): 21 | """RGB color type. 22 | 23 | rgb = RGBColor(128, 128, 128) 24 | 25 | """ 26 | 27 | __slots__ = ['_red', '_green', '_blue'] 28 | 29 | def _get_red(self): 30 | return self._red 31 | 32 | def _set_red(self, red): 33 | red = int(red) 34 | if red < 0 or red > 255: 35 | raise ValueError(red) 36 | self._red = red 37 | return 38 | 39 | def _get_green(self): 40 | return self._green 41 | 42 | def _set_green(self, green): 43 | green = int(green) 44 | if green < 0 or green > 255: 45 | raise ValueError(green) 46 | self._green = green 47 | return 48 | 49 | def _get_blue(self): 50 | return self._blue 51 | 52 | def _set_blue(self, blue): 53 | blue = int(blue) 54 | if blue < 0 or blue > 255: 55 | raise ValueError(blue) 56 | self._blue = blue 57 | return 58 | 59 | red = property(_get_red, _set_red, doc="Color red value.") 60 | green = property(_get_green, _set_green, doc="Color green value.") 61 | blue = property(_get_blue, _set_blue, doc="Color blue value.") 62 | 63 | def set(self, red=None, green=None, blue=None): 64 | """Set color properties.""" 65 | if red != None: 66 | self.red = red 67 | if green != None: 68 | self.green = green 69 | if blue != None: 70 | self.blue = blue 71 | 72 | def __init__(self, red=0, green=0, blue=0): 73 | """RGB color instanciation.""" 74 | self.red = red 75 | self.green = green 76 | self.blue = blue 77 | 78 | def __iter__(self): 79 | """Return iterator over red, green, blue values.""" 80 | return (x for x in (self._red, self._green, self._blue)) 81 | 82 | def __str__(self): 83 | """Return color as string (for database input).""" 84 | return '%s,%s,%s' % (self._red, self._green, self._blue) 85 | 86 | def __repr__(self): 87 | return 'oroboros.colors.RGBColor(%s, %s, %s)' % (self._red, self._green, 88 | self._blue) 89 | 90 | 91 | 92 | def _test(): 93 | import doctest 94 | doctest.testmod() 95 | 96 | 97 | if __name__ == "__main__": 98 | _test() 99 | 100 | # End. 101 | -------------------------------------------------------------------------------- /oroboros/core/astrolog32.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Import astrolog32 files. 6 | 7 | """ 8 | 9 | import os.path 10 | 11 | from oroboros.core.charts import Chart 12 | 13 | 14 | __all__ = ['load'] 15 | 16 | 17 | 18 | def load(path): 19 | """Load an astrolog32 file. Return chart (no positions calculated). 20 | 21 | :rtype: Chart 22 | """ 23 | path = os.path.abspath(os.path.expanduser(path)) 24 | f = file(path) 25 | lines = f.read().split('\n') 26 | f.close() 27 | for line in [x.strip() for x in lines]: 28 | if not line.startswith('/'): 29 | continue 30 | if line.startswith('/qb'): 31 | line = line[4:] 32 | # get date and time, latitude, longitude 33 | prts = line.split(' ') 34 | parts = list() 35 | for i, p in enumerate(prts): 36 | if p != '': 37 | parts.append(p) 38 | # datetime 39 | mth, d, y, time, stdt, offset, lon, lat = parts 40 | h, m, s = time.split(':') 41 | dt = [int(x) for x in (y, mth, d, h, m, s)] 42 | # longitude 43 | try: 44 | londg, rest = lon.split(':') 45 | lonmn, rest = rest.split("'") 46 | lonsc, londr = rest[:2], rest[-1] 47 | except ValueError: 48 | londg, lonmn, lonsc = lon.split(':') 49 | lonsc, londr = lonsc[:1], lonsc[-1] 50 | longitude = (londg, londr, lonmn, lonsc) 51 | # latitude 52 | try: 53 | latdg, rest = lat.split(':') 54 | latmn, rest = rest.split("'") 55 | latsc, latdr = rest[:2], rest[-1] 56 | except ValueError: 57 | latdg, latmn, latsc = lat.split(':') 58 | latsc, latdr = latsc[:1], latsc[-1] 59 | latitude = (latdg, latdr, latmn, latsc) 60 | # get utc offset 61 | hoffset, moffset = offset.split(':') 62 | soffset, hoffset = hoffset[0], int(hoffset[1:]) 63 | moffset = int(moffset) / 60.0 64 | hoffset += moffset 65 | if soffset == '+': # a32 inverts utc offsets 66 | hoffset = -hoffset 67 | if stdt == 'DT': 68 | hoffset += 1 69 | dst = True 70 | else: 71 | dst = False 72 | if line.startswith('/zi'): 73 | line = line[4:] 74 | name, location = line[1:-1].split('" "') 75 | try: 76 | cht = Chart(set_default=False, do_calc=False) 77 | cht.name = name 78 | cht.location = location 79 | cht.country = '?' 80 | cht.datetime = dt 81 | cht.longitude = longitude 82 | cht.latitude = latitude 83 | cht.utcoffset = hoffset 84 | cht.dst = dst 85 | cht.comment = 'Imported from Astrolog32' 86 | except: 87 | raise 88 | return cht 89 | 90 | 91 | # End. 92 | -------------------------------------------------------------------------------- /oroboros/core/orbs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Astrological orbs and orbs-modifiers. 6 | 7 | Create orbs. 8 | 9 | >>> Orb(10) 10 | oroboros.orbs.Orb("10.0") 11 | >>> try: 12 | ... Orb(40.7) # limited to 30 13 | ... except ValueError: 14 | ... print('Invalid orb value.') 15 | Invalid orb value. 16 | 17 | Create orb modifier: 18 | 19 | >>> om = OrbModifier("3") # add three degrees orbs 20 | >>> om 21 | oroboros.orbs.OrbModifier("3") 22 | >>> om = OrbModifier("-30%") # remove 30 % of orb 23 | >>> om 24 | oroboros.orbs.OrbModifier("-30%") 25 | 26 | """ 27 | 28 | import re 29 | from decimal import Decimal 30 | 31 | 32 | __all__ = ['Orb', 'OrbModifier'] 33 | 34 | 35 | # orb-modifiers regular expression 36 | _regexp_orbmod = re.compile('^[+-]?[0-9]+\.?[0-9]*[%]?$') 37 | 38 | 39 | class Orb(Decimal): 40 | """Astrological orb type.""" 41 | 42 | __slots__ = tuple() 43 | 44 | def __new__(cls, orb=0): 45 | """Orb initialization (orb between 0 and 30). 46 | 47 | :type orb: numeric 48 | """ 49 | orb = float(orb) 50 | if orb < 0 or orb > 30: # we consider larger orbs to be errors... 51 | raise ValueError('Invalid orb value %s.' % orb) 52 | return Decimal.__new__(cls, str(orb)) 53 | 54 | def __repr__(self): 55 | return "Orb('%s')" % self 56 | 57 | 58 | class OrbModifier(str): 59 | """Orb modifier type. 60 | 61 | Used to adjust orbs depending on the aspected planet. 62 | 63 | Either a float value, to add or substrat degrees (absolute), 64 | or a percentage value, to adjust the default orb (relative). 65 | 66 | """ 67 | 68 | __slots__ = tuple() 69 | 70 | def __new__(cls, modifier): 71 | """New orb modifier. 72 | 73 | :type modifier: str or numeric 74 | """ 75 | modifier = str(modifier) 76 | if _regexp_orbmod.search(modifier) == None: 77 | raise ValueError('Invalid orb modifier %s.' % modifier) 78 | return str.__new__(cls, modifier) 79 | 80 | def get_absolute(self, orb): 81 | """Get orb absolute value, compared to base orb. 82 | 83 | :type orb: numeric 84 | :rtype: Decimal 85 | """ 86 | if not self.endswith('%'): 87 | return Decimal(self) 88 | else: 89 | return (orb/Decimal('100')) * Decimal(self[:-1]) 90 | 91 | def __repr__(self): 92 | return "OrbModifier('%s')" % self 93 | 94 | 95 | 96 | def _test(): 97 | import doctest 98 | doctest.testmod() 99 | 100 | 101 | if __name__ == "__main__": 102 | _test() 103 | 104 | # End. 105 | -------------------------------------------------------------------------------- /oroboros/core/kwdict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Keywords dict objects, used for charts. 6 | 7 | >>> kwd = KeywordsDict('k1:v1;k2:v2') 8 | >>> print(kwd) 9 | k2:v2;k1:v1 10 | >>> kwd['k3'] = 'v3' 11 | >>> print(kwd) 12 | k3:v3;k2:v2;k1:v1 13 | 14 | """ 15 | 16 | 17 | __all__ = ['KeywordsDict'] 18 | 19 | 20 | class KeywordsDict(dict): 21 | """Chart keywords. 22 | 23 | >>> kwd = KeywordsDict('key:value;key2:value2') 24 | 25 | """ 26 | 27 | __slots__ = tuple() 28 | 29 | def __init__(self, kwstr=None): 30 | """Init keywords object. 31 | 32 | :type kwstr: str or None 33 | """ 34 | if kwstr != None: 35 | self._set_keywords(kwstr) 36 | 37 | def _set_keywords(self, kwstr=''): 38 | """Set dict with a string. 39 | 40 | String are like: "key:value;key:value;...". 41 | Pass an empty str to clear the dict. 42 | 43 | :type kwstr: str 44 | """ 45 | if kwstr == '': # reset 46 | self.clear() 47 | return 48 | kvlist = _serialize(kwstr).split(';') 49 | for kv in kvlist[:]: # erase empty strings 50 | if kv == '': 51 | kvlist.pop(kvlist.index(kv)) 52 | for kv in kvlist: 53 | kvs = kv.split(':') 54 | if len(kvs) > 2: # trying to get something out of errors 55 | for i in range(0, len(kvs), 2): 56 | try: 57 | self[kvs[i]] = kvs[i+1] 58 | except IndexError: # drop it. 59 | pass 60 | else: # no error in user input 61 | self[kvs[0].strip()] = kvs[1].strip() 62 | 63 | def __setitem__(self, k, v): 64 | """Set keywords item. 65 | 66 | :type k: str 67 | :type v: str 68 | """ 69 | k = k.replace(':', '').replace(';', '') 70 | v = v.replace(':', '').replace(';', '') 71 | k.strip() 72 | v.strip() 73 | dict.__setitem__(self, _serialize(k), _serialize(v)) 74 | 75 | def __str__(self): 76 | """Get keywords as str (for database input). 77 | 78 | :rtype: str 79 | """ 80 | ret = str() 81 | for k, v in self.items(): 82 | ret += '%s:%s;' % (k, v) 83 | return ret[:-1] 84 | 85 | def __repr__(self): 86 | return "KeywordsDict('''%s''')" % str(self) 87 | 88 | 89 | 90 | def _serialize(s): 91 | """Return string encoded in utf8, if it's not unicode already. 92 | 93 | :type s: str 94 | """ 95 | if not isinstance(s, unicode): 96 | return s.decode('utf-8') 97 | else: 98 | return s 99 | 100 | 101 | def _test(): 102 | import doctest 103 | doctest.testmod() 104 | 105 | if __name__ == '__main__': 106 | _test() 107 | 108 | # End. 109 | -------------------------------------------------------------------------------- /oroboros/core/skylendar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Import Skif charts. 6 | 7 | """ 8 | 9 | import os.path 10 | 11 | from oroboros.core.charts import Chart 12 | from oroboros.core import xmlutils 13 | 14 | 15 | __all__ = ['load'] 16 | 17 | 18 | def load(path): 19 | """Load skif and return chart object (not calculated). 20 | 21 | :type path: str 22 | :rtype: Chart 23 | """ 24 | path = os.path.abspath(os.path.expanduser(path)) 25 | f = xmlutils.parse(path) 26 | data = f.get_child(tag='DATASET') 27 | # name 28 | name = data.get_child_text(tag='NAME') 29 | # datetime 30 | year = int(data.get_child_attr('Year', tag='DATE')) 31 | month = int(data.get_child_attr('Month', tag='DATE')) 32 | day = int(data.get_child_attr('Day', tag='DATE')) 33 | hm = data.get_child_attr('Hm', tag='DATE') 34 | hour, minute = [int(x) for x in hm.split(':')] 35 | datetime = (year, month, day, hour, minute, 0) 36 | # location 37 | location = data.get_child_text(tag='PLACE') 38 | # country 39 | country = data.get_child_text(tag='COUNTRY') 40 | # zoneinfo, utcoffset, dst 41 | zoneinfo = data.get_child_attr('ZoneInfoFile', tag='COUNTRY') 42 | if zoneinfo == '': # need utcoffset 43 | utcoff = data.get_child_attr('Timezone', tag='DATE') 44 | h, m = [int(x) for x in utcoff.split(':')] 45 | utcoffset = -(h + (m / 60.0)) 46 | else: 47 | utcoffset = '' 48 | if data.get_child_attr('Daylight', tag='DATE') != '0:00': 49 | dst = True 50 | else: 51 | dst = False 52 | # adjust utc offset 53 | if utcoffset != '': 54 | if dst == True: 55 | utcoffset -= 1 56 | # geocoords 57 | lat = data.get_child_attr('Latitude', tag='PLACE') 58 | latdg, latmn = lat[:-1].split(':') 59 | latdr = lat[-1] 60 | latitude = (int(latdg), latdr, int(latmn), 0) 61 | lon = data.get_child_attr('Longitude', tag='PLACE') 62 | londg, lonmn = lon[:-1].split(':') 63 | londr = lon[-1] 64 | longitude = (int(londg), londr, int(lonmn), 0) 65 | # comments 66 | comment = 'Imported from Skylendar.\n' 67 | comment += 'Chart type: %s\n' % data.get_child_text(tag='TYPE') 68 | comment += 'Gender: %s\n' % data.get_child_attr('Gender', tag='TYPE') 69 | comment += 'Keywords: %s\n' % data.get_child_text(tag='KEYWORDS') 70 | comment += 'Comment: %s' % data.get_child_text(tag='COMMENT') 71 | # ... 72 | cht = Chart(set_default=False, do_calc=False) 73 | cht.set(name=name, datetime=datetime, location=location, country=country, 74 | zoneinfo=zoneinfo, dst=dst, utcoffset=utcoffset, latitude=latitude, 75 | longitude=longitude, altitude=0, comment=comment) 76 | return cht 77 | 78 | 79 | # End. 80 | -------------------------------------------------------------------------------- /oroboros/gui/orbfiltersmandialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Orbs filters manager. 6 | 7 | """ 8 | 9 | import sys 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | from oroboros.core.orbsfilters import OrbsFilter, all_orbs_filters_names 15 | from oroboros.gui.orbfilterdialog import OrbsFilterDialog 16 | 17 | 18 | __all__ = ['OrbsFiltersManagerDialog'] 19 | 20 | 21 | 22 | class OrbsFiltersManagerDialog(QDialog): 23 | 24 | def __init__(self, parent=None): 25 | QDialog.__init__(self, parent) 26 | self._parent = parent 27 | tr = self.tr 28 | self.setWindowTitle(tr('Orbs Filters Manager')) 29 | self.setSizeGripEnabled(True) 30 | # layout 31 | grid = QGridLayout(self) 32 | self.setLayout(grid) 33 | # list of filters 34 | self.filtersBox = QComboBox(self) 35 | self.filtersBox.setEditable(False) 36 | self.filtersBox.addItems(all_orbs_filters_names()) 37 | grid.addWidget(self.filtersBox, 0, 0) 38 | # buttons 39 | buttonsLayout = QHBoxLayout() 40 | grid.addLayout(buttonsLayout, 1, 0) 41 | newButton = QPushButton(tr('New'), self) 42 | self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent) 43 | buttonsLayout.addWidget(newButton) 44 | editButton = QPushButton(tr('Edit'), self) 45 | self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent) 46 | buttonsLayout.addWidget(editButton) 47 | deleteButton = QPushButton(tr('Delete'), self) 48 | self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent) 49 | buttonsLayout.addWidget(deleteButton) 50 | closeButton = QPushButton(tr('Close'), self) 51 | closeButton.setDefault(True) 52 | self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()')) 53 | buttonsLayout.addWidget(closeButton) 54 | 55 | def newFilterEvent(self): 56 | new = OrbsFilterDialog(self) 57 | ok = new.exec_() 58 | if ok: 59 | self.reset() 60 | 61 | def editFilterEvent(self): 62 | filt = OrbsFilter( 63 | all_orbs_filters_names()[self.filtersBox.currentIndex()]) 64 | edit = OrbsFilterDialog(self, filt) 65 | ok = edit.exec_() 66 | if ok: 67 | self.reset() 68 | 69 | def deleteFilterEvent(self): 70 | tr = self.tr 71 | name = all_orbs_filters_names()[self.filtersBox.currentIndex()] 72 | ret = QMessageBox.warning(self, tr('Delete Orbs Filter'), 73 | unicode(tr('Are you sure you want to delete orbs filter \xab %(filter)s \xbb ?')) % { 74 | 'filter': name}, 75 | QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes) 76 | if ret == QMessageBox.Yes: 77 | filt = OrbsFilter(name) 78 | try: 79 | filt.delete() 80 | except ValueError: # cannot delete default filter 81 | QMessageBox.critical(self, tr('Required Orbs Filter'), 82 | tr('Cannot delete required filter.')) 83 | self.reset() 84 | 85 | def reset(self): 86 | """Reload filters list.""" 87 | self.filtersBox.clear() 88 | self.filtersBox.addItems(all_orbs_filters_names()) 89 | 90 | 91 | 92 | def main(): 93 | app = QApplication(sys.argv) 94 | main = OrbsFiltersManagerDialog() 95 | main.show() 96 | sys.exit(app.exec_()) 97 | 98 | 99 | if __name__ == '__main__': 100 | main() 101 | 102 | # End. 103 | -------------------------------------------------------------------------------- /oroboros/gui/aspfiltersmandialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Aspects filters manager. 6 | 7 | """ 8 | 9 | import sys 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | from oroboros.core.aspectsfilters import AspectsFilter, all_aspects_filters_names 15 | from oroboros.gui.aspfilterdialog import AspectsFilterDialog 16 | 17 | 18 | __all__ = ['AspectsFiltersManagerDialog'] 19 | 20 | 21 | class AspectsFiltersManagerDialog(QDialog): 22 | 23 | def __init__(self, parent=None): 24 | QDialog.__init__(self, parent) 25 | self._parent = parent 26 | tr = self.tr 27 | self.setWindowTitle(tr('Aspects Filters Manager')) 28 | self.setSizeGripEnabled(True) 29 | # layout 30 | grid = QGridLayout(self) 31 | self.setLayout(grid) 32 | # list of filters 33 | self.filtersBox = QComboBox(self) 34 | self.filtersBox.setEditable(False) 35 | self.filtersBox.addItems(all_aspects_filters_names()) 36 | grid.addWidget(self.filtersBox, 0, 0) 37 | # buttons 38 | buttonsLayout = QHBoxLayout() 39 | grid.addLayout(buttonsLayout, 1, 0) 40 | newButton = QPushButton(tr('New'), self) 41 | self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent) 42 | buttonsLayout.addWidget(newButton) 43 | editButton = QPushButton(tr('Edit'), self) 44 | self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent) 45 | buttonsLayout.addWidget(editButton) 46 | deleteButton = QPushButton(tr('Delete'), self) 47 | self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent) 48 | buttonsLayout.addWidget(deleteButton) 49 | closeButton = QPushButton(tr('Close'), self) 50 | closeButton.setDefault(True) 51 | self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()')) 52 | buttonsLayout.addWidget(closeButton) 53 | 54 | def newFilterEvent(self): 55 | new = AspectsFilterDialog(self) 56 | ok = new.exec_() 57 | if ok: 58 | self.reset() 59 | 60 | def editFilterEvent(self): 61 | filt = AspectsFilter( 62 | all_aspects_filters_names()[self.filtersBox.currentIndex()]) 63 | edit = AspectsFilterDialog(self, filt) 64 | ok = edit.exec_() 65 | if ok: 66 | self.reset() 67 | 68 | def deleteFilterEvent(self): 69 | tr = self.tr 70 | name = all_aspects_filters_names()[self.filtersBox.currentIndex()] 71 | ret = QMessageBox.warning(self, tr('Delete Aspects Filter'), 72 | unicode(tr('Are you sure you want to delete aspects filter \xab %(filter)s \xbb ?')) % { 73 | 'filter': name}, 74 | QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes) 75 | if ret == QMessageBox.Yes: 76 | filt = AspectsFilter(name) 77 | try: 78 | filt.delete() 79 | except ValueError: # cannot delete default filter 80 | QMessageBox.critical(self, tr('Required Aspects Filter'), 81 | tr('Cannot delete required filter.')) 82 | self.reset() 83 | 84 | def reset(self): 85 | """Reload filters list.""" 86 | self.filtersBox.clear() 87 | self.filtersBox.addItems(all_aspects_filters_names()) 88 | 89 | 90 | 91 | def main(): 92 | app = QApplication(sys.argv) 93 | main = PlanetsFiltersManagerDialog() 94 | main.show() 95 | sys.exit(app.exec_()) 96 | 97 | 98 | if __name__ == '__main__': 99 | main() 100 | 101 | # End. 102 | -------------------------------------------------------------------------------- /oroboros/gui/plntfiltersmandialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Planets filters manager. 6 | 7 | """ 8 | 9 | import sys 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | from oroboros.core.planetsfilters import PlanetsFilter, all_planets_filters_names 15 | from oroboros.gui.plntfilterdialog import PlanetsFilterDialog 16 | 17 | 18 | __all__ = ['PlanetsFiltersManagerDialog'] 19 | 20 | 21 | 22 | class PlanetsFiltersManagerDialog(QDialog): 23 | 24 | def __init__(self, parent=None): 25 | QDialog.__init__(self, parent) 26 | self._parent = parent 27 | tr = self.tr 28 | self.setWindowTitle(tr('Planets Filters Manager')) 29 | self.setSizeGripEnabled(True) 30 | # layout 31 | grid = QGridLayout(self) 32 | self.setLayout(grid) 33 | # list of filters 34 | self.filtersBox = QComboBox(self) 35 | self.filtersBox.setEditable(False) 36 | self.filtersBox.addItems(all_planets_filters_names()) 37 | grid.addWidget(self.filtersBox, 0, 0) 38 | # buttons 39 | buttonsLayout = QHBoxLayout() 40 | grid.addLayout(buttonsLayout, 1, 0) 41 | newButton = QPushButton(tr('New'), self) 42 | self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent) 43 | buttonsLayout.addWidget(newButton) 44 | editButton = QPushButton(tr('Edit'), self) 45 | self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent) 46 | buttonsLayout.addWidget(editButton) 47 | deleteButton = QPushButton(tr('Delete'), self) 48 | self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent) 49 | buttonsLayout.addWidget(deleteButton) 50 | closeButton = QPushButton(tr('Close'), self) 51 | closeButton.setDefault(True) 52 | self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()')) 53 | buttonsLayout.addWidget(closeButton) 54 | 55 | def newFilterEvent(self): 56 | new = PlanetsFilterDialog(self) 57 | ok = new.exec_() 58 | if ok: 59 | self.reset() 60 | 61 | def editFilterEvent(self): 62 | filt = PlanetsFilter( 63 | all_planets_filters_names()[self.filtersBox.currentIndex()]) 64 | edit = PlanetsFilterDialog(self, filt) 65 | ok = edit.exec_() 66 | if ok: 67 | self.reset() 68 | 69 | def deleteFilterEvent(self): 70 | tr = self.tr 71 | name = all_planets_filters_names()[self.filtersBox.currentIndex()] 72 | ret = QMessageBox.warning(self, tr('Delete Planets Filter'), 73 | unicode(tr('Are you sure you want to delete planets filter \xab %(filter)s \xbb ?')) % { 74 | 'filter': name}, 75 | QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes) 76 | if ret == QMessageBox.Yes: 77 | filt = PlanetsFilter(name) 78 | try: 79 | filt.delete() 80 | except ValueError: # cannot delete default filter 81 | QMessageBox.critical(self, tr('Required Planets Filter'), 82 | tr('Cannot delete required filter.')) 83 | self.reset() 84 | 85 | def reset(self): 86 | """Reload filters list.""" 87 | self.filtersBox.clear() 88 | self.filtersBox.addItems(all_planets_filters_names()) 89 | 90 | 91 | 92 | def main(): 93 | app = QApplication(sys.argv) 94 | main = PlanetsFiltersManagerDialog() 95 | main.show() 96 | sys.exit(app.exec_()) 97 | 98 | 99 | if __name__ == '__main__': 100 | main() 101 | 102 | # End. 103 | -------------------------------------------------------------------------------- /oroboros/gui/midpfiltersmandialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Mid-points filters manager. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | from oroboros.core.midpfilters import MidPointsFilter, all_midpoints_filters_names 13 | from oroboros.gui.midpfilterdialog import MidPointsFilterDialog 14 | 15 | 16 | __all__ = ['MidPointsFiltersManagerDialog'] 17 | 18 | 19 | 20 | class MidPointsFiltersManagerDialog(QDialog): 21 | 22 | def __init__(self, parent=None): 23 | QDialog.__init__(self, parent) 24 | self._parent = parent 25 | tr = self.tr 26 | self.setWindowTitle(tr('MidPoints Filters Manager')) 27 | self.setSizeGripEnabled(True) 28 | # layout 29 | grid = QGridLayout(self) 30 | self.setLayout(grid) 31 | # list of filters 32 | self.filtersBox = QComboBox(self) 33 | self.filtersBox.setEditable(False) 34 | self.filtersBox.addItems(all_midpoints_filters_names()) 35 | grid.addWidget(self.filtersBox, 0, 0) 36 | # buttons 37 | buttonsLayout = QHBoxLayout() 38 | grid.addLayout(buttonsLayout, 1, 0) 39 | newButton = QPushButton(tr('New'), self) 40 | self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent) 41 | buttonsLayout.addWidget(newButton) 42 | editButton = QPushButton(tr('Edit'), self) 43 | self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent) 44 | buttonsLayout.addWidget(editButton) 45 | deleteButton = QPushButton(tr('Delete'), self) 46 | self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent) 47 | buttonsLayout.addWidget(deleteButton) 48 | closeButton = QPushButton(tr('Close'), self) 49 | closeButton.setDefault(True) 50 | self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()')) 51 | buttonsLayout.addWidget(closeButton) 52 | 53 | def newFilterEvent(self): 54 | new = MidPointsFilterDialog(self) 55 | ok = new.exec_() 56 | if ok: 57 | self.reset() 58 | 59 | def editFilterEvent(self): 60 | filt = MidPointsFilter( 61 | all_midpoints_filters_names()[self.filtersBox.currentIndex()]) 62 | edit = MidPointsFilterDialog(self, filt) 63 | ok = edit.exec_() 64 | if ok: 65 | self.reset() 66 | 67 | def deleteFilterEvent(self): 68 | tr = self.tr 69 | name = all_midpoints_filters_names()[self.filtersBox.currentIndex()] 70 | ret = QMessageBox.warning(self, tr('Delete MidPoints Filter'), 71 | unicode(tr('Are you sure you want to delete mid-points filter \xab %(filter)s \xbb ?')) % { 72 | 'filter': name}, 73 | QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes) 74 | if ret == QMessageBox.Yes: 75 | filt = MidPointsFilter(name) 76 | try: 77 | filt.delete() 78 | except ValueError: # cannot delete default filter 79 | QMessageBox.critical(self, tr('Default MidPoints Filter'), 80 | tr('Cannot delete filter used as default.')) 81 | self.reset() 82 | 83 | def reset(self): 84 | """Reload filters list.""" 85 | self.filtersBox.clear() 86 | self.filtersBox.addItems(all_midpoints_filters_names()) 87 | 88 | 89 | 90 | def main(): 91 | import sys 92 | app = QApplication(sys.argv) 93 | main = MidPointsFiltersManagerDialog() 94 | main.show() 95 | sys.exit(app.exec_()) 96 | 97 | 98 | if __name__ == '__main__': 99 | main() 100 | 101 | # End. 102 | -------------------------------------------------------------------------------- /oroboros/gui/orbrestrmandialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Orbs restrictions manager. 6 | 7 | """ 8 | 9 | import sys 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | from oroboros.core.orbsrestrictions import OrbsRestrictions, all_orbs_restrictions_names 15 | from oroboros.gui.orbrestrdialog import OrbsRestrictionsDialog 16 | 17 | 18 | __all__ = ['OrbsRestrictionsManagerDialog'] 19 | 20 | 21 | 22 | class OrbsRestrictionsManagerDialog(QDialog): 23 | 24 | def __init__(self, parent=None): 25 | QDialog.__init__(self, parent) 26 | self._parent = parent 27 | tr = self.tr 28 | self.setWindowTitle(tr('Orbs Restrictions Manager')) 29 | self.setSizeGripEnabled(True) 30 | # layout 31 | grid = QGridLayout(self) 32 | self.setLayout(grid) 33 | # list of filters 34 | self.filtersBox = QComboBox(self) 35 | self.filtersBox.setEditable(False) 36 | self.filtersBox.addItems(all_orbs_restrictions_names()) 37 | grid.addWidget(self.filtersBox, 0, 0) 38 | # buttons 39 | buttonsLayout = QHBoxLayout() 40 | grid.addLayout(buttonsLayout, 1, 0) 41 | newButton = QPushButton(tr('New'), self) 42 | self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent) 43 | buttonsLayout.addWidget(newButton) 44 | editButton = QPushButton(tr('Edit'), self) 45 | self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent) 46 | buttonsLayout.addWidget(editButton) 47 | deleteButton = QPushButton(tr('Delete'), self) 48 | self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent) 49 | buttonsLayout.addWidget(deleteButton) 50 | closeButton = QPushButton(tr('Close'), self) 51 | closeButton.setDefault(True) 52 | self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()')) 53 | buttonsLayout.addWidget(closeButton) 54 | 55 | def newFilterEvent(self): 56 | new = OrbsRestrictionsDialog(self) 57 | ok = new.exec_() 58 | if ok: 59 | self.reset() 60 | 61 | def editFilterEvent(self): 62 | filt = OrbsRestrictions( 63 | all_orbs_restrictions_names()[self.filtersBox.currentIndex()]) 64 | edit = OrbsRestrictionsDialog(self, filt) 65 | ok = edit.exec_() 66 | if ok: 67 | self.reset() 68 | 69 | def deleteFilterEvent(self): 70 | tr = self.tr 71 | name = all_orbs_restrictions_names()[self.filtersBox.currentIndex()] 72 | ret = QMessageBox.warning(self, tr('Delete Orbs Restrictions'), 73 | unicode(tr('Are you sure you want to delete orbs restrictions \xab %(filter)s \xbb ?')) % { 74 | 'filter': name}, 75 | QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes) 76 | if ret == QMessageBox.Yes: 77 | filt = OrbsRestrictions(name) 78 | try: 79 | filt.delete() 80 | except ValueError: # cannot delete 81 | QMessageBox.critical(self, tr('Required Orbs Restrictions'), 82 | tr('Cannot delete required orbs restrictions.')) 83 | self.reset() 84 | 85 | def reset(self): 86 | """Reload filters list.""" 87 | self.filtersBox.clear() 88 | self.filtersBox.addItems(all_orbs_restrictions_names()) 89 | 90 | 91 | 92 | def main(): 93 | app = QApplication(sys.argv) 94 | main = OrbsRestrictionsManagerDialog() 95 | main.show() 96 | sys.exit(app.exec_()) 97 | 98 | 99 | if __name__ == '__main__': 100 | main() 101 | 102 | # End. 103 | -------------------------------------------------------------------------------- /oroboros/gui/profectiondialog.py: -------------------------------------------------------------------------------- 1 | #!/sur/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | """ 5 | Profection dialog. 6 | 7 | """ 8 | 9 | import sys 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | 15 | __all__ = ['ProfectionDialog'] 16 | 17 | 18 | class ProfectionDialog(QDialog): 19 | 20 | def __init__(self, parent=None): 21 | QDialog.__init__(self, parent) 22 | tr = self.tr 23 | self.setWindowTitle(tr('Add Degrees')) 24 | layout = QGridLayout(self) 25 | self.setLayout(layout) 26 | # input value 27 | layout.addWidget(QLabel(tr('Value')), 0, 0) 28 | self.valueEdit = QDoubleSpinBox(self) 29 | self.valueEdit.setRange(-360, 360) 30 | self.valueEdit.setSuffix(tr('\xb0', 'Degrees')) 31 | self.valueEdit.setDecimals(6) 32 | self.valueEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus) 33 | self.valueEdit.setValue(30) 34 | layout.addWidget(self.valueEdit, 0, 1) 35 | # profection mode 36 | self.profMode = QCheckBox(tr('Profection'), self) 37 | self.connect(self.profMode, SIGNAL('stateChanged(int)'), 38 | self.setProfMode) 39 | layout.addWidget(self.profMode, 1, 0) 40 | # profection unit 41 | self.profUnit = QComboBox(self) 42 | self.profUnit.setEditable(False) 43 | self.profUnit.setDisabled(True) 44 | units = [tr('Per year'), tr('Per day'), tr('Per hour')] 45 | self.profUnit.addItems(units) 46 | layout.addWidget(self.profUnit, 1, 1) 47 | # datetime 48 | layout.addWidget(QLabel(tr('DateTime')), 2, 0) 49 | self.datetimeEdit = QDateTimeEdit(QDateTime.currentDateTime(), self) 50 | self.datetimeEdit.setCalendarPopup(True) 51 | self.datetimeEdit.setDisplayFormat(tr('yyyy-MM-dd hh:mm:ss', 52 | 'Datetime format')) 53 | self.datetimeEdit.setMinimumDateTime(QDateTime(-5400, 1, 1, 0, 0)) 54 | self.datetimeEdit.setMaximumDateTime(QDateTime(5400, 1, 1, 0, 0)) 55 | self.datetimeEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus) 56 | self.datetimeEdit.setDisabled(True) 57 | layout.addWidget(self.datetimeEdit, 2, 1) 58 | # buttons 59 | buttonsLayout = QHBoxLayout() 60 | layout.addLayout(buttonsLayout, 3, 0, 1, 2) 61 | cancelButton = QPushButton(tr('Cancel'), self) 62 | self.connect(cancelButton, SIGNAL('clicked()'), self.reject) 63 | buttonsLayout.addWidget(cancelButton) 64 | okButton = QPushButton(tr('Ok'), self) 65 | okButton.setDefault(True) 66 | self.connect(okButton, SIGNAL('clicked()'), self.accept) 67 | buttonsLayout.addWidget(okButton) 68 | 69 | def setProfMode(self, i): 70 | """Enable/disable profection.""" 71 | if self.profMode.isChecked(): 72 | self.profUnit.setEnabled(True) 73 | self.datetimeEdit.setEnabled(True) 74 | else: 75 | self.profUnit.setDisabled(True) 76 | self.datetimeEdit.setDisabled(True) 77 | 78 | def exec_(self): 79 | """Return ok, value, profection, profection unit, datetime.""" 80 | ok = QDialog.exec_(self) 81 | if ok: 82 | ret = (QDialog.Accepted, self.valueEdit.value(), 83 | self.profMode.isChecked(), self.profUnit.currentIndex(), 84 | self.datetimeEdit.dateTime().toPyDateTime()) 85 | return ret 86 | else: 87 | return QDialog.Rejected, 0, False, -1, 0 88 | 89 | 90 | def main(): 91 | app = QApplication(sys.argv) 92 | main = ProfectionDialog() 93 | main.show() 94 | sys.exit(app.exec_()) 95 | 96 | 97 | if __name__ == '__main__': 98 | main() 99 | 100 | # End. 101 | -------------------------------------------------------------------------------- /oroboros/gui/asprestrmandialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Aspects restrictions manager. 6 | 7 | """ 8 | 9 | import sys 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | from oroboros.core.aspectsrestrictions import AspectsRestrictions, all_aspects_restrictions_names 15 | from oroboros.gui.asprestrdialog import AspectsRestrictionsDialog 16 | 17 | 18 | __all__ = ['AspectsRestrictionsManagerDialog'] 19 | 20 | 21 | class AspectsRestrictionsManagerDialog(QDialog): 22 | 23 | def __init__(self, parent=None): 24 | QDialog.__init__(self, parent) 25 | self._parent = parent 26 | tr = self.tr 27 | self.setWindowTitle(tr('Aspects Restrictions Manager')) 28 | self.setSizeGripEnabled(True) 29 | # layout 30 | grid = QGridLayout(self) 31 | self.setLayout(grid) 32 | # list of filters 33 | self.filtersBox = QComboBox(self) 34 | self.filtersBox.setEditable(False) 35 | self.filtersBox.addItems(all_aspects_restrictions_names()) 36 | grid.addWidget(self.filtersBox, 0, 0) 37 | # buttons 38 | buttonsLayout = QHBoxLayout() 39 | grid.addLayout(buttonsLayout, 1, 0) 40 | newButton = QPushButton(tr('New'), self) 41 | self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent) 42 | buttonsLayout.addWidget(newButton) 43 | editButton = QPushButton(tr('Edit'), self) 44 | self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent) 45 | buttonsLayout.addWidget(editButton) 46 | deleteButton = QPushButton(tr('Delete'), self) 47 | self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent) 48 | buttonsLayout.addWidget(deleteButton) 49 | closeButton = QPushButton(tr('Close'), self) 50 | closeButton.setDefault(True) 51 | self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()')) 52 | buttonsLayout.addWidget(closeButton) 53 | 54 | def newFilterEvent(self): 55 | new = AspectsRestrictionsDialog(self) 56 | ok = new.exec_() 57 | if ok: 58 | self.reset() 59 | 60 | def editFilterEvent(self): 61 | filt = AspectsRestrictions( 62 | all_aspects_restrictions_names()[self.filtersBox.currentIndex()]) 63 | edit = AspectsRestrictionsDialog(self, filt) 64 | ok = edit.exec_() 65 | if ok: 66 | self.reset() 67 | 68 | def deleteFilterEvent(self): 69 | tr = self.tr 70 | name = all_aspects_restrictions_names()[self.filtersBox.currentIndex()] 71 | ret = QMessageBox.warning(self, tr('Delete Aspects Restrictions'), 72 | unicode(tr('Are you sure you want to delete aspects restrictions \xab %(filter)s \xbb ?')) % { 73 | 'filter': name}, 74 | QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes) 75 | if ret == QMessageBox.Yes: 76 | filt = AspectsRestrictions(name) 77 | try: 78 | filt.delete() 79 | except ValueError: # cannot delete default filter 80 | QMessageBox.critical(self, tr('Required Aspects Restrictions'), 81 | tr('Cannot delete required aspects restrictions.')) 82 | self.reset() 83 | 84 | def reset(self): 85 | """Reload filters list.""" 86 | self.filtersBox.clear() 87 | self.filtersBox.addItems(all_aspects_restrictions_names()) 88 | 89 | 90 | 91 | def main(): 92 | app = QApplication(sys.argv) 93 | main = AspectsRestrictionsManagerDialog() 94 | main.show() 95 | sys.exit(app.exec_()) 96 | 97 | 98 | if __name__ == '__main__': 99 | main() 100 | 101 | # End. 102 | -------------------------------------------------------------------------------- /oroboros/gui/harmonicsdialog.py: -------------------------------------------------------------------------------- 1 | #!/sur/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | """ 5 | Multiply/profection dialog. 6 | 7 | """ 8 | 9 | import sys 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | 15 | __all__ = ['HarmonicsDialog'] 16 | 17 | 18 | class HarmonicsDialog(QDialog): 19 | 20 | def __init__(self, parent=None): 21 | QDialog.__init__(self, parent) 22 | tr = self.tr 23 | self.setWindowTitle(tr('Multiply Degrees')) 24 | layout = QGridLayout(self) 25 | self.setLayout(layout) 26 | # input value 27 | layout.addWidget(QLabel(tr('Value')), 0, 0) 28 | self.valueEdit = QDoubleSpinBox(self) 29 | self.valueEdit.setRange(0, 360) 30 | self.valueEdit.setSuffix(tr('\xb0', 'Degrees')) 31 | self.valueEdit.setDecimals(6) 32 | self.valueEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus) 33 | self.valueEdit.setValue(2) 34 | layout.addWidget(self.valueEdit, 0, 1) 35 | # profection mode 36 | self.profMode = QCheckBox(tr('Profection'), self) 37 | self.connect(self.profMode, SIGNAL('stateChanged(int)'), 38 | self.setProfMode) 39 | layout.addWidget(self.profMode, 1, 0) 40 | # profection unit 41 | self.profUnit = QComboBox(self) 42 | self.profUnit.setEditable(False) 43 | self.profUnit.setDisabled(True) 44 | units = [tr('Per year'), tr('Per day'), tr('Per hour')] 45 | self.profUnit.addItems(units) 46 | layout.addWidget(self.profUnit, 1, 1) 47 | # datetime 48 | layout.addWidget(QLabel(tr('DateTime')), 2, 0) 49 | self.datetimeEdit = QDateTimeEdit(QDateTime.currentDateTime(), self) 50 | self.datetimeEdit.setCalendarPopup(True) 51 | self.datetimeEdit.setDisplayFormat(tr('yyyy-MM-dd hh:mm:ss', 52 | 'Datetime format')) 53 | self.datetimeEdit.setMinimumDateTime(QDateTime(-5400, 1, 1, 0, 0)) 54 | self.datetimeEdit.setMaximumDateTime(QDateTime(5400, 1, 1, 0, 0)) 55 | self.datetimeEdit.setButtonSymbols(QAbstractSpinBox.PlusMinus) 56 | self.datetimeEdit.setDisabled(True) 57 | layout.addWidget(self.datetimeEdit, 2, 1) 58 | # buttons 59 | buttonsLayout = QHBoxLayout() 60 | layout.addLayout(buttonsLayout, 3, 0, 1, 2) 61 | cancelButton = QPushButton(tr('Cancel'), self) 62 | self.connect(cancelButton, SIGNAL('clicked()'), self.reject) 63 | buttonsLayout.addWidget(cancelButton) 64 | okButton = QPushButton(tr('Ok'), self) 65 | okButton.setDefault(True) 66 | self.connect(okButton, SIGNAL('clicked()'), self.accept) 67 | buttonsLayout.addWidget(okButton) 68 | 69 | def setProfMode(self, i): 70 | """Enable/disable profection.""" 71 | if self.profMode.isChecked(): 72 | self.profUnit.setEnabled(True) 73 | self.datetimeEdit.setEnabled(True) 74 | else: 75 | self.profUnit.setDisabled(True) 76 | self.datetimeEdit.setDisabled(True) 77 | 78 | def exec_(self): 79 | """Return ok, value, profection, profection unit, datetime.""" 80 | ok = QDialog.exec_(self) 81 | if ok: 82 | ret = (QDialog.Accepted, self.valueEdit.value(), 83 | self.profMode.isChecked(), self.profUnit.currentIndex(), 84 | self.datetimeEdit.dateTime().toPyDateTime()) 85 | return ret 86 | else: 87 | return QDialog.Rejected, 0, False, -1, 0 88 | 89 | 90 | 91 | def main(): 92 | app = QApplication(sys.argv) 93 | main = HarmonicsDialog() 94 | main.show() 95 | sys.exit(app.exec_()) 96 | 97 | 98 | if __name__ == '__main__': 99 | main() 100 | 101 | # End. 102 | -------------------------------------------------------------------------------- /oroboros/gui/menubar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | """ 5 | Menu bar. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | 13 | __all__ = ['createMenuBar'] 14 | 15 | 16 | def createMenuBar(self): 17 | """Create menu bar.""" 18 | tr = self.tr 19 | menuBar = self.menuBar() 20 | # charts menu 21 | fileMenu = menuBar.addMenu(tr('&Desktop')) 22 | fileMenu.addAction(self.actionNewMultiChart) 23 | fileMenu.addAction(self.actionHereNowMultiChart) 24 | fileMenu.addAction(self.actionOpenMultiChart) 25 | fileMenu.addAction(self.actionCustomMultiChart) 26 | ## chart imports 27 | importMenu = QMenu(tr('Import...'), self) 28 | importMenu.addAction(self.actionOpenMultiChartA32) 29 | importMenu.addAction(self.actionOpenMultiChartSkif) 30 | fileMenu.addMenu(importMenu) 31 | ## chart exports 32 | exportMenu = QMenu(tr('Export...'), self) 33 | exportMenu.addAction(self.actionSaveImage) 34 | fileMenu.addMenu(exportMenu) 35 | fileMenu.addSeparator() 36 | fileMenu.addAction(self.actionCloseMultiChart) 37 | fileMenu.addSeparator() 38 | fileMenu.addAction(self.actionExit) 39 | # subcharts menu 40 | chartMenu = menuBar.addMenu(tr('&SubCharts')) 41 | chart1Menu = QMenu(tr('Chart 1'), self) 42 | chart1Menu.addAction(self.actionOpenChart1) 43 | chart1Menu.addAction(self.actionHideChart1) 44 | chart1Menu.addAction(self.actionEditChart1) 45 | chart1Menu.addAction(self.actionFilterChart1) 46 | chart1Menu.addAction(self.actionSaveChart1) 47 | chart1Menu.addAction(self.actionSaveChart1As) 48 | chart1Menu.addAction(self.actionCloseChart1) 49 | chartMenu.addMenu(chart1Menu) 50 | chart2Menu = QMenu(tr('Chart 2'), self) 51 | chart2Menu.addAction(self.actionOpenChart2) 52 | chart2Menu.addAction(self.actionHideChart2) 53 | chart2Menu.addAction(self.actionEditChart2) 54 | chart2Menu.addAction(self.actionFilterChart2) 55 | chart2Menu.addAction(self.actionSaveChart2) 56 | chart2Menu.addAction(self.actionSaveChart2As) 57 | chart2Menu.addAction(self.actionCloseChart2) 58 | chartMenu.addMenu(chart2Menu) 59 | chartMenu.addAction(self.actionSwitchCharts) 60 | chartMenu.addSeparator() 61 | # compare submenu 62 | compMenu = QMenu(tr('Compare'), self) 63 | compMenu.addAction(self.actionTransitMode) 64 | compMenu.addAction(self.actionProgressionMode) 65 | compMenu.addAction(self.actionDirectionMode) 66 | chartMenu.addMenu(compMenu) 67 | # transform submenu 68 | transMenu = QMenu(tr('Transform'), self) 69 | transMenu.addAction(self.actionMultiplyPos) 70 | transMenu.addAction(self.actionAddPos) 71 | chartMenu.addMenu(transMenu) 72 | # merge submenu 73 | mergeMenu = QMenu(tr('Merge'), self) 74 | mergeMenu.addAction(self.actionComposite) 75 | mergeMenu.addAction(self.actionMidSpaceTime) 76 | chartMenu.addMenu(mergeMenu) 77 | # configuration menu 78 | cfgMenu = menuBar.addMenu(tr('&Configuration')) 79 | cfgMenu.addAction(self.actionSettings) 80 | cfgMenu.addAction(self.actionFilters) 81 | cfgMenu.addSeparator() 82 | cfgMenu.addAction(self.actionPlanetsFilters) 83 | cfgMenu.addAction(self.actionAspectsFilters) 84 | cfgMenu.addAction(self.actionOrbsFilters) 85 | cfgMenu.addAction(self.actionAspectsRestrictions) 86 | cfgMenu.addAction(self.actionOrbsRestrictions) 87 | cfgMenu.addAction(self.actionMidPointsFilters) 88 | # about menu 89 | aboutMenu = menuBar.addMenu(tr('About')) 90 | aboutMenu.addAction(self.actionAboutQt) 91 | aboutMenu.addAction(self.actionAboutOroboros) 92 | 93 | 94 | 95 | 96 | # End. 97 | -------------------------------------------------------------------------------- /oroboros/gui/chtflowers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Rearrange planets disposition. 6 | 7 | """ 8 | 9 | import swisseph as swe 10 | 11 | 12 | __all__ = ['rearrange'] 13 | 14 | 15 | def rearrange(L, w): 16 | """Return list of rearranged positions.""" 17 | bunches = BunchBunch(L, w) 18 | return bunches.offer() 19 | 20 | 21 | 22 | class BunchBunch(list): 23 | 24 | def __init__(self, L, w): 25 | self.L = L 26 | if len(L) > 36: # the usual width of 10px is cracked 27 | w = 360.0 / len(L) 28 | self.w = w 29 | for p in L: 30 | self.addflower(p) 31 | self.arrange() 32 | 33 | def addflower(self, p): 34 | if len(self) == 0: 35 | self.newbunch(p) 36 | else: 37 | for b in self: 38 | if b.inbunch(p): 39 | b.addflower(p) 40 | return 41 | self.newbunch(p) 42 | 43 | def newbunch(self, pos): 44 | self.append(Bunch(self.w, pos)) 45 | 46 | def arrange(self): 47 | test = True 48 | while test: 49 | test = self._arrange() 50 | if test == False: 51 | self.reverse() 52 | test = self._arrange() 53 | 54 | def _arrange(self): 55 | for b1 in self[:-1]: 56 | for b2 in self[1:]: 57 | if b1 == b2: 58 | continue 59 | if b1.mixbunch(b2): 60 | self.pop(self.index(b1)) 61 | self.pop(self.index(b2)) 62 | b1.addbunch(b2) 63 | self.append(b1) 64 | return True 65 | return False 66 | 67 | def offer(self): 68 | ret = [None] * len(self.L) 69 | for b in [x.arrange() for x in self]: 70 | for k, v in b: 71 | key = None 72 | start = -1 73 | while key == None: 74 | idx = self.L.index(k, start+1) 75 | if ret[idx] == None: 76 | key = idx 77 | else: 78 | start = idx 79 | ret[key] = v 80 | return ret 81 | 82 | 83 | 84 | class Bunch(list): 85 | 86 | def __init__(self, w, pos): 87 | self.w = w 88 | self.center = pos 89 | self.append(pos) 90 | 91 | def addflower(self, pos): 92 | self.append(pos) 93 | self.calc_center() 94 | 95 | def calc_center(self): 96 | tmp = 0 97 | for p1 in self[:-1]: 98 | for p2 in self[1:]: 99 | dist = abs(swe.difdeg2n(p1, p2)) 100 | if dist >= tmp: 101 | maxw = (p1, p2) 102 | tmp = dist 103 | self.center = swe.deg_midp(*maxw) 104 | 105 | def inbunch(self, pos): 106 | test = swe._match_aspect(pos, 0, self.center, 0, 0, self.orb()) 107 | if test[0] != None: 108 | return True 109 | return False 110 | 111 | def mixbunch(self, other): 112 | test = swe._match_aspect(self.center, 0, other.center, 0, 113 | 0, self.orb()+other.orb()) 114 | if test[0] != None: 115 | return True 116 | return False 117 | 118 | def orb(self): 119 | return (self.w * len(self)) / 2.0 120 | 121 | def addbunch(self, other): 122 | list.extend(self, other) 123 | self.calc_center() 124 | 125 | def arrange(self): 126 | self.sort(self.cmp) 127 | ret = list() 128 | dist = self.orb() - (self.w / 2.0) 129 | for i in range(len(self)): 130 | ret.append((self[i], swe.degnorm(self.center - dist))) 131 | dist -= self.w 132 | return ret 133 | 134 | def cmp(self, x , y): 135 | t1 = swe.difdeg2n(x, self.center) 136 | t2 = swe.difdeg2n(y, self.center) 137 | if t1 > t2: 138 | return 1 139 | elif t1 < t2: 140 | return -1 141 | else: 142 | return 0 143 | 144 | 145 | 146 | def _test(): 147 | import doctest 148 | doctest.testmod() 149 | 150 | 151 | if __name__ == '__main__': 152 | _test() 153 | 154 | # End. 155 | -------------------------------------------------------------------------------- /oroboros/gui/filtersmandialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Filters manager. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | from oroboros.core.filters import Filter, all_filters_names 13 | from oroboros.gui.filterdialog import FilterDialog 14 | 15 | 16 | __all__ = ['FiltersManagerDialog'] 17 | 18 | 19 | class FiltersManagerDialog(QDialog): 20 | 21 | def __init__(self, parent=None, mode='man'): 22 | QDialog.__init__(self, parent) 23 | self._parent = parent 24 | self._mode = mode # either 'man' or 'select' 25 | tr = self.tr 26 | self.setWindowTitle(tr('Filters Manager')) 27 | self.setSizeGripEnabled(True) 28 | # layout 29 | grid = QGridLayout(self) 30 | self.setLayout(grid) 31 | # list of filters 32 | self.filtersBox = QComboBox(self) 33 | self.filtersBox.setEditable(False) 34 | self.filtersBox.addItems(all_filters_names()) 35 | grid.addWidget(self.filtersBox, 0, 0) 36 | # buttons 37 | buttonsLayout = QHBoxLayout() 38 | grid.addLayout(buttonsLayout, 1, 0) 39 | newButton = QPushButton(tr('New'), self) 40 | self.connect(newButton, SIGNAL('clicked()'), self.newFilterEvent) 41 | buttonsLayout.addWidget(newButton) 42 | editButton = QPushButton(tr('Edit'), self) 43 | self.connect(editButton, SIGNAL('clicked()'), self.editFilterEvent) 44 | buttonsLayout.addWidget(editButton) 45 | deleteButton = QPushButton(tr('Delete'), self) 46 | self.connect(deleteButton, SIGNAL('clicked()'), self.deleteFilterEvent) 47 | buttonsLayout.addWidget(deleteButton) 48 | if mode == 'man': 49 | closeButton = QPushButton(tr('Close'), self) 50 | self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()')) 51 | elif mode == 'select': 52 | closeButton = QPushButton(tr('Select'), self) 53 | self.connect(closeButton, SIGNAL('clicked()'), self.accept) 54 | closeButton.setDefault(True) 55 | buttonsLayout.addWidget(closeButton) 56 | 57 | def newFilterEvent(self): 58 | new = FilterDialog(self) 59 | ok = new.exec_() 60 | if ok: 61 | self.reset() 62 | 63 | def editFilterEvent(self): 64 | filt = Filter(all_filters_names()[self.filtersBox.currentIndex()]) 65 | edit = FilterDialog(self, filt) 66 | ok = edit.exec_() 67 | if ok: 68 | self.reset() 69 | 70 | def deleteFilterEvent(self): 71 | tr = self.tr 72 | name = all_filters_names()[self.filtersBox.currentIndex()] 73 | ret = QMessageBox.warning(self, tr('Delete Filter'), 74 | unicode(tr('Are you sure you want to delete filter \xab %(filter)s \xbb ?')) % { 75 | 'filter': name}, 76 | QMessageBox.Cancel|QMessageBox.Yes, QMessageBox.Yes) 77 | if ret == QMessageBox.Yes: 78 | filt = Filter(name) 79 | try: 80 | filt.delete() 81 | except ValueError: # cannot delete 82 | QMessageBox.critical(self, tr('Default Filter'), 83 | tr('Cannot delete default filter.')) 84 | self.reset() 85 | 86 | def reset(self): 87 | """Reload filters list.""" 88 | self.filtersBox.clear() 89 | self.filtersBox.addItems(all_filters_names()) 90 | 91 | def exec_(self): 92 | if self._mode == 'select': 93 | ok = QDialog.exec_(self) 94 | if ok: 95 | name = all_filters_names()[self.filtersBox.currentIndex()] 96 | return ok, name 97 | else: 98 | return ok, None 99 | else: 100 | return QDialog.exec_(self) 101 | 102 | 103 | 104 | def main(): 105 | import sys 106 | app = QApplication(sys.argv) 107 | main = FiltersManagerDialog() 108 | main.show() 109 | sys.exit(app.exec_()) 110 | 111 | 112 | if __name__ == '__main__': 113 | main() 114 | 115 | # End. 116 | -------------------------------------------------------------------------------- /oroboros/core/timezone.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Timezone system. 6 | 7 | The timezones are used to compute local mean time (and to 8 | provide some more info about charts). 9 | 10 | See: 11 | - http://www.worldtimezone.net/standard.html 12 | - http://en.wikipedia.org/wiki/Timezone 13 | 14 | Implemented are nowadays standard timezones. 15 | Historical timezones (eg. méridien de Paris) may easily be 16 | added to the end of the list. 17 | Please send any update to the software maintainer... 18 | 19 | """ 20 | 21 | __all__ = ['TimeZone', 22 | 'all_timezones', 'get'] 23 | 24 | 25 | class TimeZone(object): 26 | """Timezone object.""" 27 | 28 | __slots__ = ('code', 'utc', 'offset', 'longitude') 29 | 30 | def __init__(self, code, utc, offset, longitude): 31 | """Init TimeZone object. 32 | 33 | :type code: str 34 | :type utc: str 35 | :type offset: numeric 36 | :type longitude: numeric 37 | """ 38 | self.code = code # code name 39 | self.utc = utc # utc name 40 | self.offset = offset # offset 41 | self.longitude = longitude # longitude 42 | 43 | def __str__(self): 44 | """Get a printable version of timezone. 45 | 46 | :rtype: str 47 | """ 48 | return '%s (%s)' % (self.code, self.utc) 49 | 50 | def __repr__(self): 51 | return 'TimeZone(%s, %s, %s, %s)' % ( 52 | self.code, self.utc, self.offset, self.longitude) 53 | 54 | 55 | 56 | all_timezones = [ 57 | # code utc.code offset longitude 58 | TimeZone('Z', 'UTC+0', 0, 0), 59 | TimeZone('A', 'UTC+1', 1, 15), 60 | TimeZone('B', 'UTC+2', 2, 30), 61 | TimeZone('C', 'UTC+3', 3, 45), 62 | TimeZone('C+', 'UTC+3:30', 3.5, 52.5), ## Iran 63 | TimeZone('D', 'UTC+4', 4, 60), 64 | TimeZone('D+', 'UTC+4:30', 4.5, 67.5), ## Afghanistan 65 | TimeZone('E', 'UTC+5', 5, 75), 66 | TimeZone('E+', 'UTC+5:30', 5.5, 62.5), ## India, etc 67 | TimeZone('E++', 'UTC+5:45', 5.75, 66.25), ## Nepal 68 | TimeZone('F', 'UTC+6', 6, 90), 69 | TimeZone('F+', 'UTC+6:30', 6.5, 97.5), ## Burma 70 | TimeZone('G', 'UTC+7', 7, 105), 71 | TimeZone('H', 'UTC+8', 8, 120), 72 | TimeZone('H++', 'UTC+8:45', 8.75, 131.25), ## Adelaide 73 | TimeZone('I', 'UTC+9', 9, 135), 74 | TimeZone('I+', 'UTC+9:30', 9.5, 142.5), ## Alice Springs 75 | TimeZone('K', 'UTC+10', 10, 150), 76 | TimeZone('K+', 'UTC+10:30', 10.5, 157.5), ## Lord Howe Isl. 77 | TimeZone('L', 'UTC+11', 11, 165), 78 | TimeZone('L+', 'UTC+11:30', 11.5, 172.5), ## Norfolk Isl. 79 | TimeZone('M', 'UTC+12', 12, 180), 80 | TimeZone('M++', 'UTC+12:45', 12.75, -169.75), ## Chatham Isl. 81 | TimeZone('M+', 'UTC+13', 13, -165), ## Phoenix Isl. 82 | TimeZone('M+', 'UTC+14', 14, -150), ## Line Isl. 83 | TimeZone('N', 'UTC-1', -1, -15), 84 | TimeZone('O', 'UTC-2', -2, -30), 85 | TimeZone('P', 'UTC-3', -3, -45), 86 | TimeZone('P+', 'UTC-3:30', -3.5, -52.5), ## Labrador 87 | TimeZone('Q', 'UTC-4', -4, -60), 88 | TimeZone('Q+', 'UTC-4:30', -4.5, -67.5), ## Venezuela 89 | TimeZone('R', 'UTC-5', -5, -75), 90 | TimeZone('S', 'UTC-6', -6, -90), 91 | TimeZone('T', 'UTC-7', -7, -105), 92 | TimeZone('U', 'UTC-8', -8, -120), 93 | TimeZone('V', 'UTC-9', -9, -135), 94 | TimeZone('V+', 'UTC-9:30', -9.5, -142.5), ## Marquesas Isl. 95 | TimeZone('W', 'UTC-10', -10, -150), 96 | TimeZone('X', 'UTC-11', -11, -165), 97 | TimeZone('Y', 'UTC-12', -12, -180) 98 | ] 99 | 100 | 101 | def get(zone): 102 | """Find corresponding TimeZone object for given Utc code name. 103 | 104 | :type zone: str 105 | :rtype: TimeZone 106 | :raise ValueError: not found 107 | """ 108 | for tz in all_timezones: 109 | if tz.utc == zone: 110 | return tz 111 | raise ValueError('Invalid utc zone %s.' % zone) 112 | 113 | 114 | # End. 115 | -------------------------------------------------------------------------------- /oroboros/gui/saveimagedialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Save image dialog. 6 | 7 | """ 8 | 9 | import os.path 10 | 11 | from PyQt4.QtCore import * 12 | from PyQt4.QtGui import * 13 | 14 | from oroboros.core import cfg 15 | 16 | 17 | __all__ = ['SaveImageDialog'] 18 | 19 | 20 | _iconsDir = os.path.join(os.path.dirname(__file__), 'icons') 21 | 22 | 23 | class SaveImageDialog(QDialog): 24 | 25 | def __init__(self, parent=None): 26 | QDialog.__init__(self, parent) 27 | tr = self.tr 28 | self.setWindowTitle(tr('Save Image')) 29 | self.setSizeGripEnabled(True) 30 | self.setMinimumWidth(250) 31 | # layout 32 | grid = QGridLayout(self) 33 | self.setLayout(grid) 34 | # file name 35 | grid.addWidget(QLabel(tr('File Name')), 0, 0) 36 | self.fname = QLineEdit(self) 37 | self.fname.setReadOnly(True) 38 | grid.addWidget(self.fname, 0, 1) 39 | # file chooser 40 | chooseButton = QToolButton(self) 41 | chooseButton.setIcon(QIcon(os.path.join(_iconsDir, 'gtk-open.png'))) 42 | chooseButton.setToolTip(tr('Get file name')) 43 | self.connect(chooseButton, SIGNAL('clicked()'), self.getFileName) 44 | grid.addWidget(chooseButton, 0, 2) 45 | # extension/format 46 | grid.addWidget(QLabel(tr('Extension')), 1, 0) 47 | self.extBox = QComboBox(self) 48 | self.extBox.setEditable(False) 49 | self.extBox.addItems( 50 | ['png', 'jpg', 'bmp', 'ppm', 'tiff', 'xbm', 'xpm', 'svg']) 51 | grid.addWidget(self.extBox, 1, 1, 1, 2) 52 | # width 53 | grid.addWidget(QLabel(tr('Width')), 2, 0) 54 | self.widthBox = QSpinBox(self) 55 | self.widthBox.setRange(1, 10000) 56 | self.widthBox.setSuffix(tr('px', 'Pixels')) 57 | self.widthBox.setButtonSymbols(QAbstractSpinBox.PlusMinus) 58 | self.widthBox.setValue(600) 59 | grid.addWidget(self.widthBox, 2, 1, 1, 2) 60 | # height 61 | grid.addWidget(QLabel(tr('Height')), 3, 0) 62 | self.heightBox = QSpinBox(self) 63 | self.heightBox.setRange(1, 10000) 64 | self.heightBox.setSuffix(tr('px', 'Pixels')) 65 | self.heightBox.setButtonSymbols(QAbstractSpinBox.PlusMinus) 66 | self.heightBox.setValue(600) 67 | grid.addWidget(self.heightBox, 3, 1, 1, 2) 68 | # buttons 69 | buttonsLayout = QHBoxLayout() 70 | grid.addLayout(buttonsLayout, 4, 0, 1, 3) 71 | cancelButton = QPushButton(tr('Cancel'), self) 72 | self.connect(cancelButton, SIGNAL('clicked()'), self.reject) 73 | buttonsLayout.addWidget(cancelButton) 74 | okButton = QPushButton(tr('Save'), self) 75 | okButton.setDefault(True) 76 | self.connect(okButton, SIGNAL('clicked()'), self.accept) 77 | buttonsLayout.addWidget(okButton) 78 | 79 | def getFileName(self): 80 | tr = self.tr 81 | path = unicode(QFileDialog.getSaveFileName(self, 82 | tr('Save Image As...'), 83 | os.path.expanduser(cfg.charts_dir), 84 | tr('Images (*.png *.jpg *.bmp *.ppm *.tiff *.xbm *.xpm *.svg)'))) 85 | if path != '': 86 | self.fname.setText(path) 87 | 88 | def accept(self): 89 | """Check input data.""" 90 | if unicode(self.fname.text()) == '': 91 | QMessageBox.critical(self, self.tr('Missing File Name'), 92 | self.tr('Please set file name.')) 93 | self.fname.setFocus() 94 | return 95 | else: 96 | QDialog.accept(self) 97 | 98 | def exec_(self): 99 | ok = QDialog.exec_(self) 100 | if not ok: 101 | return ok, None, None, None, None 102 | else: 103 | fname = unicode(self.fname.text()) 104 | ext = unicode(self.extBox.currentText()) 105 | w = self.widthBox.value() 106 | h = self.heightBox.value() 107 | return ok, fname, ext, w, h 108 | 109 | 110 | 111 | def main(): 112 | import sys 113 | app = QApplication(sys.argv) 114 | main = SaveImageDialog() 115 | main.show() 116 | sys.exit(app.exec_()) 117 | 118 | if __name__ == '__main__': 119 | main() 120 | 121 | # End. 122 | -------------------------------------------------------------------------------- /oroboros/core/charts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Chart objects. 6 | 7 | """ 8 | 9 | from oroboros.core.chartcalc import ChartCalc 10 | from oroboros.core.aspectsresults import AspectDataList 11 | from oroboros.core.results import PlanetDataList 12 | 13 | 14 | __all__ = ['Chart'] 15 | 16 | 17 | class Chart(ChartCalc): 18 | """Chart object including display and drawing helpers.""" 19 | 20 | __slots__ = ChartCalc.__slots__ + ['_hidden'] 21 | 22 | def _get_hidden(self): 23 | """Get hidden flag. 24 | 25 | :rtype: bool 26 | """ 27 | return self._hidden 28 | 29 | def _set_hidden(self, boolean): 30 | """Set hidden flag. 31 | 32 | :type boolean: bool 33 | """ 34 | self._hidden = bool(boolean) 35 | 36 | # from ChartFile 37 | path = ChartCalc.path 38 | name = ChartCalc.name 39 | datetime = ChartCalc.datetime 40 | calendar = ChartCalc.calendar 41 | location = ChartCalc.location 42 | latitude = ChartCalc.latitude 43 | longitude = ChartCalc.longitude 44 | altitude = ChartCalc.altitude 45 | country = ChartCalc.country 46 | zoneinfo = ChartCalc.zoneinfo 47 | timezone = ChartCalc.timezone 48 | comment = ChartCalc.comment 49 | keywords = ChartCalc.keywords 50 | dst = ChartCalc.dst 51 | utcoffset = ChartCalc.utcoffset 52 | # from ChartDate 53 | julday = ChartCalc.julday 54 | local_datetime = ChartCalc.local_datetime 55 | utc_datetime = ChartCalc.utc_datetime 56 | local_mean_datetime = ChartCalc.local_mean_datetime 57 | sidtime = ChartCalc.sidtime 58 | local_sidtime = ChartCalc.local_sidtime 59 | # from ChartCalc 60 | filter = ChartCalc.filter 61 | ecl_nut = ChartCalc.ecl_nut 62 | planets = ChartCalc.planets 63 | houses = ChartCalc.houses 64 | aspects = ChartCalc.aspects 65 | midpoints = ChartCalc.midpoints 66 | midp_aspects = ChartCalc.midp_aspects 67 | # Additional properties 68 | hidden = property(_get_hidden, _set_hidden, 69 | doc='Chart hidden state flag.') 70 | 71 | def __init__(self, path=None, set_default=True, do_calc=True): 72 | ChartCalc.__init__(self, path, set_default, do_calc) 73 | self._hidden = False 74 | 75 | def hide(self): 76 | """Set hidden flag to True.""" 77 | self._hidden = True 78 | 79 | def show(self): 80 | """Set hidden flag to False.""" 81 | self._hidden = False 82 | 83 | def _all_draw_planets(self): 84 | """Return a list of all points to draw (incl. midpoints). 85 | 86 | :rtype: PlanetDataList 87 | """ 88 | ret = PlanetDataList() 89 | ret.extend(self._planets) 90 | if self._filter._draw_midp: 91 | ret.extend(self._midp_aspects.get_midpoints()) 92 | return ret 93 | 94 | def _all_draw_aspects(self): 95 | """Return a list of all aspects to draw (incl. midpoints). 96 | 97 | :rtype: AspectDataList 98 | """ 99 | ret = AspectDataList() 100 | ret.extend(self._aspects) 101 | if self._filter._draw_midp: 102 | ret.extend(self._midp_aspects) 103 | return ret 104 | 105 | def __iter__(self): 106 | """Iterate over chart properties. 107 | 108 | :rtype: generator 109 | """ 110 | return (x for x in (self._path, self._name, self._datetime, 111 | self._calendar, self._location, self._latitude, self._longitude, 112 | self._altitude, self._country, self._zoneinfo, self._timezone, 113 | self._comment, self._keywords, self._dst, self._utcoffset, 114 | self._julday, self._local_datetime, self._utc_datetime, 115 | self._local_mean_datetime, self._sidtime, self._local_sidtime, 116 | self._filter, self._ecl_nut, self._houses, self._planets, 117 | self._aspects, self._midpoints, self._midp_aspects, self._hidden)) 118 | 119 | def __repr__(self): 120 | if self._path != None: 121 | return "Chart('''%s''')" % self._path 122 | else: 123 | return repr(tuple(repr(x) for x in self)) 124 | 125 | 126 | 127 | 128 | 129 | # End. 130 | -------------------------------------------------------------------------------- /oroboros/gui/coordswidget.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Editable boxes for geocoords. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | from oroboros.core.geocoords import Latitude, Longitude 13 | 14 | 15 | __all__ = ['LatitudeEdit', 'LongitudeEdit'] 16 | 17 | 18 | class _CoordsWidget(QHBoxLayout): 19 | """Base class. 20 | 21 | coords -> Latitude or Longitude object 22 | 23 | """ 24 | 25 | def __init__(self, coords, parent): 26 | QHBoxLayout.__init__(self) 27 | if isinstance(coords, Latitude): 28 | self._typ = 'lat' 29 | elif isinstance(coords, Longitude): 30 | self._typ = 'lon' 31 | else: 32 | raise TypeError('Invalid coords %s.' % coords) 33 | self._coords = coords 34 | tr = self.tr 35 | # set params 36 | if self._typ == 'lat': 37 | maxdg = 90 38 | self.drlist = ['N', 'S'] 39 | strlst = [tr('N', 'North'), tr('S', 'South')] 40 | elif self._typ == 'lon': 41 | maxdg = 180 42 | self.drlist = ['E', 'W'] 43 | strlst = [tr('E', 'East'), tr('W', 'West')] 44 | # degrees 45 | self.dgspin = QSpinBox(parent) 46 | self.dgspin.setMaximum(maxdg) 47 | self.dgspin.setMinimum(0) 48 | self.dgspin.setSuffix(tr('\xb0', 'Degrees')) 49 | self.dgspin.setAccelerated(True) 50 | self.dgspin.setButtonSymbols(QAbstractSpinBox.PlusMinus) 51 | self.addWidget(self.dgspin) 52 | # direction 53 | self.drbox = QComboBox(parent) 54 | self.drbox.addItems(strlst) 55 | self.drbox.setEditable(False) 56 | self.addWidget(self.drbox) 57 | # minutes 58 | self.mnspin = QSpinBox(parent) 59 | self.mnspin.setMaximum(59) 60 | self.mnspin.setMinimum(0) 61 | self.mnspin.setSuffix(tr("'", 'Minutes')) 62 | self.mnspin.setAccelerated(True) 63 | self.mnspin.setButtonSymbols(QAbstractSpinBox.PlusMinus) 64 | self.addWidget(self.mnspin) 65 | # seconds 66 | self.scspin = QSpinBox(parent) 67 | self.scspin.setMaximum(59) 68 | self.scspin.setMinimum(0) 69 | self.scspin.setSuffix(tr('"', 'Seconds')) 70 | self.scspin.setAccelerated(True) 71 | self.scspin.setButtonSymbols(QAbstractSpinBox.PlusMinus) 72 | self.addWidget(self.scspin) 73 | # set values 74 | self.reset() 75 | 76 | def reset(self): 77 | self.dgspin.setValue(self._coords._degrees) 78 | if self._coords._direction == self.drlist[0]: 79 | self.drbox.setCurrentIndex(0) 80 | else: 81 | self.drbox.setCurrentIndex(1) 82 | self.mnspin.setValue(self._coords._minutes) 83 | self.scspin.setValue(self._coords._seconds) 84 | 85 | def value(self): 86 | dg = self.dgspin.value() 87 | mn = self.mnspin.value() 88 | sc = self.scspin.value() 89 | if self.drbox.currentIndex() == 0: 90 | if self._typ == 'lat': 91 | dr = 'N' 92 | else: 93 | dr = 'E' 94 | else: 95 | if self._typ == 'lat': 96 | dr = 'S' 97 | else: 98 | dr = 'W' 99 | if self._typ == 'lat': 100 | return Latitude(dg, dr, mn, sc) 101 | else: 102 | return Longitude(dg, dr, mn, sc) 103 | 104 | 105 | 106 | 107 | class LatitudeEdit(_CoordsWidget): 108 | 109 | def latitude(self): 110 | return _CoordsWidget.value(self) 111 | 112 | def setLatitude(self, lat): 113 | self._coords = lat 114 | self.reset() 115 | 116 | 117 | class LongitudeEdit(_CoordsWidget): 118 | 119 | def longitude(self): 120 | return _CoordsWidget.value(self) 121 | 122 | def setLongitude(self, lon): 123 | self._coords = lon 124 | self.reset() 125 | 126 | 127 | class AltitudeEdit(QSpinBox): 128 | 129 | def __init__(self, alt, parent): 130 | QSpinBox.__init__(self, parent) 131 | self._alt = alt 132 | self.setMaximum(10000) 133 | self.setMinimum(0) 134 | self.setSuffix(self.tr(' m.', 'meters')) 135 | self.setAccelerated(True) 136 | self.setButtonSymbols(QAbstractSpinBox.PlusMinus) 137 | self.reset() 138 | 139 | def reset(self): 140 | self.setValue(int(self._alt)) 141 | 142 | def altitude(self): 143 | return QSpinBox.value(self) 144 | 145 | def setAltitude(self, alt): 146 | self._alt = alt 147 | self.reset() 148 | 149 | 150 | # End. 151 | -------------------------------------------------------------------------------- /oroboros/gui/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Application manager. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | from oroboros.core import cfg 13 | from oroboros.core import desktop 14 | 15 | 16 | ##__all__ = [] 17 | 18 | 19 | # singleton 20 | mainwin = None ## main window 21 | 22 | 23 | def appendMultiChart(cht): 24 | desktop.charts.append(cht) 25 | mainwin.addTabs(len(desktop.charts) - 1) 26 | 27 | def insertMultiChart(idx, cht): 28 | desktop.charts.insert(idx, cht) 29 | mainwin.addTabs(idx) 30 | 31 | def replaceMultiChart(idx, cht): 32 | desktop.charts[idx] = cht 33 | mainwin.resetTabs(idx) 34 | 35 | def removeMultiChart(idx): 36 | del(desktop.charts[idx]) 37 | mainwin.removeTabs(idx) 38 | 39 | def replaceChart(idx, num, cht): 40 | try: 41 | desktop.charts[idx][num] = cht 42 | except IndexError: 43 | desktop.charts[idx].append(cht) 44 | mainwin.resetTabs(idx) 45 | 46 | def removeChart(idx, num): 47 | del(desktop.charts[idx][num]) 48 | mainwin.resetTabs(idx) 49 | 50 | 51 | 52 | 53 | def filterUpdatedEvent(idx): 54 | for i, cht in enumerate(desktop.charts): 55 | rset = False 56 | for c in cht: 57 | reset = False 58 | if c._filter._idx_ == idx: 59 | c._filter.reset() 60 | rset, reset = True, True 61 | if reset: 62 | c.calc() 63 | if rset: 64 | cht.calc() 65 | mainwin.resetTabs(i) 66 | 67 | 68 | def planetsFilterUpdatedEvent(idx): 69 | for i, cht in enumerate(desktop.charts): 70 | rset = False 71 | for c in cht: 72 | reset = False 73 | if c._filter._planets._idx_ == idx: 74 | c._filter._planets.reset() 75 | rset, reset = True, True 76 | if c._filter._midpoints._planets._idx_ == idx: 77 | c._filter._midpoints._planets.reset() 78 | rset, reset = True, True 79 | if reset: 80 | c.calc() 81 | if rset: 82 | cht.calc() 83 | mainwin.resetTabs(i) 84 | 85 | 86 | def aspectsFilterUpdatedEvent(idx): 87 | for i, cht in enumerate(desktop.charts): 88 | rset = False 89 | for c in cht: 90 | reset = False 91 | if c._filter._aspects._idx_ == idx: 92 | c._filter._aspects.reset() 93 | rset, reset = True, True 94 | if c._filter._midpoints._aspects._idx_ == idx: 95 | c._filter._midpoints._aspects.reset() 96 | rset, reset = True, True 97 | if reset: 98 | c.calc() 99 | if rset: 100 | cht.calc() 101 | mainwin.resetTabs(i) 102 | 103 | 104 | def orbsFilterUpdatedEvent(idx): 105 | for i, cht in enumerate(desktop.charts): 106 | rset = False 107 | for c in cht: 108 | reset = False 109 | if c._filter._orbs._idx_ == idx: 110 | c._filter._orbs.reset() 111 | rset, reset = True, True 112 | if c._filter._midpoints._orbs._idx_ == idx: 113 | c._filter._midpoints._orbs.reset() 114 | rset, reset = True, True 115 | if reset: 116 | c.calc() 117 | if rset: 118 | cht.calc() 119 | mainwin.resetTabs(i) 120 | 121 | 122 | def aspectsRestrictionsUpdatedEvent(idx): 123 | for i, cht in enumerate(desktop.charts): 124 | rset = False 125 | for c in cht: 126 | reset = False 127 | if c._filter._asprestr._idx_ == idx: 128 | c._filter._asprestr.reset() 129 | rset, reset = True, True 130 | if c._filter._midpoints._asprestr._idx_ == idx: 131 | c._filter._midpoints._asprestr.reset() 132 | rset, reset = True, True 133 | if reset: 134 | c.calc() 135 | if rset: 136 | cht.calc() 137 | mainwin.resetTabs(i) 138 | 139 | 140 | def orbsRestrictionsUpdatedEvent(idx): 141 | for i, cht in enumerate(desktop.charts): 142 | rset = False 143 | for c in cht: 144 | reset = False 145 | if c._filter._orbrestr._idx_ == idx: 146 | c._filter._orbrestr.reset() 147 | rset, reset = True, True 148 | if c._filter._midpoints._orbrestr._idx_ == idx: 149 | c._filter._midpoints._orbrestr.reset() 150 | rset, reset = True, True 151 | if reset: 152 | c.calc() 153 | if rset: 154 | cht.calc() 155 | mainwin.resetTabs(i) 156 | 157 | 158 | def midPointsFilterUpdatedEvent(idx): 159 | for i, cht in enumerate(desktop.charts): 160 | rset = False 161 | for c in cht: 162 | reset = False 163 | if c._filter._midpoints._idx_ == idx: 164 | c._filter._midpoints.reset() 165 | rset, reset = True, True 166 | if reset: 167 | c.calc() 168 | if rset: 169 | cht.calc() 170 | mainwin.resetTabs(i) 171 | 172 | 173 | # End. 174 | -------------------------------------------------------------------------------- /oroboros/gui/geonames.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | GeoNames.org query interface. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | from oroboros.core import geonames 13 | 14 | 15 | __all__ = ['GeoNamesQueryDialog'] 16 | 17 | 18 | class GeoNamesQueryDialog(QDialog): 19 | 20 | def __init__(self, parent=None): 21 | QDialog.__init__(self, parent) 22 | tr = self.tr 23 | self._parent = parent 24 | self.setWindowTitle(tr('Query GeoNames.org')) 25 | self.setSizeGripEnabled(True) 26 | self.setMinimumWidth(400) 27 | self._results = list() # geonames results 28 | self._sel = None # selected result 29 | # layout 30 | grid = QGridLayout(self) 31 | self.setLayout(grid) 32 | # search 33 | grid.addWidget(QLabel(tr('Search for')), 0, 0) 34 | self.nameQuery = QLineEdit(self) 35 | grid.addWidget(self.nameQuery, 0, 1) 36 | # results 37 | grid.addWidget(QLabel(tr('Results')), 1, 0) 38 | self.resultsBox = QComboBox(self) 39 | self.resultsBox.setEditable(False) 40 | grid.addWidget(self.resultsBox, 1, 1) 41 | # buttons 42 | buttonsLayout = QHBoxLayout() 43 | grid.addLayout(buttonsLayout, 2, 0, 1, 2) 44 | searchButton = QPushButton(tr('Search'), self) 45 | searchButton.setDefault(True) 46 | self.connect(searchButton, SIGNAL('clicked()'), self.searchEvent) 47 | buttonsLayout.addWidget(searchButton) 48 | self.selectButton = QPushButton(tr('Select'), self) 49 | self.selectButton.setDisabled(True) 50 | self.connect(self.selectButton, SIGNAL('clicked()'), self.selectEvent) 51 | buttonsLayout.addWidget(self.selectButton) 52 | closeButton = QPushButton(tr('Close'), self) 53 | self.connect(closeButton, SIGNAL('clicked()'), SLOT('close()')) 54 | buttonsLayout.addWidget(closeButton) 55 | 56 | def searchEvent(self): 57 | tr = self.tr 58 | txt = unicode(self.nameQuery.text()).strip() 59 | if txt == '': # no input 60 | self.selectButton.setDisabled(True) 61 | self.nameQuery.setFocus() 62 | return 63 | # threading 64 | th = SearchThread(txt, self) 65 | progDialog = ProgressDialog(self) 66 | self.connect(th, SIGNAL('finished()'), progDialog, SLOT('accept()')) 67 | self.connect(th, SIGNAL('terminated()'), progDialog, SLOT('reject()')) 68 | self.connect(progDialog, SIGNAL('rejected()'), th, SLOT('quit()')) 69 | th.start() 70 | ok = progDialog.exec_() 71 | if not ok: # aborted or error 72 | return 73 | # display results 74 | fmt = unicode( 75 | tr('%(name)s, %(cty)s, %(lat)s, %(lon)s, %(alt)s %(m)s, %(tz)s')) 76 | all = [fmt % { 77 | 'name': x[0], 78 | 'cty': x[1], 79 | 'lat': str(x[2]), 80 | 'lon': str(x[3]), 81 | 'alt': str(x[4]), 82 | 'm': tr('m.', 'Meters'), 83 | 'tz': x[5] 84 | } for x in self._results] 85 | self.resultsBox.clear() 86 | if len(all) != 0: 87 | self.resultsBox.addItems(all) 88 | self.selectButton.setEnabled(True) 89 | else: 90 | self.selectButton.setDisabled(True) 91 | 92 | def selectEvent(self): 93 | """Set parent's geonames results.""" 94 | try: 95 | self._sel = self._results[self.resultsBox.currentIndex()] 96 | except IndexError: # should not happen 97 | self.done(QDialog.Rejected) 98 | self.done(QDialog.Accepted) 99 | 100 | def exec_(self): 101 | ok = QDialog.exec_(self) 102 | if ok: 103 | return ok, self._sel 104 | else: 105 | return ok, None 106 | 107 | 108 | class SearchThread(QThread): 109 | """Query geonames.org in a thread.""" 110 | 111 | def __init__(self, txt, parent): 112 | QThread.__init__(self, parent) 113 | self._parent = parent 114 | self._txt = txt.encode('utf-8') 115 | 116 | def run(self): 117 | try: 118 | self._parent._results = geonames.search(self._txt) 119 | except ValueError: # not connected? 120 | #raise 121 | self.terminate()##self.emit(SIGNAL('terminated()')) 122 | 123 | 124 | class ProgressDialog(QDialog): 125 | """Progress bar dialog.""" 126 | 127 | def __init__(self, parent): 128 | QDialog.__init__(self, parent) 129 | self.setWindowTitle(self.tr('Waiting...', 'Progress bar')) 130 | layout = QHBoxLayout(self) 131 | self.setLayout(layout) 132 | progBar = QProgressBar(self) 133 | progBar.setRange(0, 0) 134 | layout.addWidget(progBar) 135 | 136 | 137 | 138 | def main(): 139 | import sys 140 | app = QApplication(sys.argv) 141 | main = GeoNamesQueryDialog() 142 | main.show() 143 | sys.exit(app.exec_()) 144 | 145 | if __name__ == '__main__': 146 | main() 147 | 148 | # End. 149 | -------------------------------------------------------------------------------- /oroboros/core/hgrepo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Synchronize charts directory using Mercurial. 6 | 7 | Little how-to: 8 | 9 | - Setup your Mercurial server. 10 | - Setup a server repo for your charts, accessible by http. 11 | See ``oroboros-hg --init`` command, or use hg itself. 12 | Ex: ``su -c "oroboros-hg --init=/path/to/hg/repo/charts" apache`` 13 | - Set your Hg repo settings with the graphical interface. 14 | If you want to pull/push changes automagically, edit your 15 | settings so that your charts directory points to the local repo. 16 | - Clone the main repo in your charts directory. 17 | See ``oroboros-hg --clone`` or use hg itself. 18 | 19 | """ 20 | 21 | import os 22 | import os.path 23 | 24 | from oroboros.core import cfg 25 | 26 | __all__ = ['init', 'clone'] 27 | 28 | 29 | # hgignore file 30 | _hgignore = """# Oroboros Charts Repo - hgignore 31 | syntax: glob 32 | # backups 33 | *~ 34 | *.bak 35 | # images 36 | *.png 37 | *.jpg 38 | *.bmp 39 | *.tiff 40 | *.ppm 41 | *.xpm 42 | *.xbm 43 | *.svg 44 | """ 45 | 46 | # hgrc file 47 | _hgrc = """[web] 48 | name = %s 49 | contact = %s 50 | description = %s 51 | allow_push = %s 52 | deny_push = %s 53 | push_ssl = %s 54 | """ 55 | 56 | # commit message 57 | _commit_msg = "Auto-commit by Oroboros" 58 | 59 | 60 | def init(path=None, allow=['*'], deny=[], ssl=False): 61 | """Init hg charts repository. 62 | 63 | By default, anybody is allowed to push changes. 64 | 65 | :type path: str or None 66 | :type allow: sequence 67 | :type deny: sequence 68 | :type ssl: bool 69 | """ 70 | if path == None: # use cfg charts dir 71 | path = os.path.abspath(os.path.expanduser(cfg.charts_dir)) 72 | else: 73 | path = os.path.abspath(os.path.expanduser(path)) 74 | # check dir exists 75 | if not os.path.isdir(path): 76 | os.mkdir(path) 77 | # copy hgignore 78 | f = open(os.path.join(path, '.hgignore'), 'w') 79 | f.write(_hgignore) 80 | f.close() 81 | # init 82 | os.system('hg init %s' % path) 83 | # copy hgrc 84 | hgrc = os.path.join(path, '.hg', 'hgrc') 85 | f = open(hgrc, 'w') 86 | f.write(_hgrc % ('Oroboros-charts', cfg.usermail, 'Oroboros charts', 87 | ','.join(allow), ','.join(deny), str(ssl))) 88 | f.close() 89 | # commit 90 | commit(path) 91 | 92 | 93 | def commit(path=None): 94 | """Commit local changes. 95 | 96 | :type path: str or None 97 | """ 98 | if path == None: 99 | path = cfg.charts_dir 100 | os.system('hg commit -A -m "%s" -R %s' % ( 101 | _commit_msg, path)) 102 | 103 | 104 | def clone(src=None, dest=None, user=None, pswd=None): 105 | """Clone distant repository. 106 | 107 | :type src: str or None 108 | :type dest: str or None 109 | :type user: str or None 110 | :type pswd: str or None 111 | """ 112 | if src == None: 113 | src = cfg.hg_repo 114 | if dest == None: 115 | dest = os.path.abspath(os.path.expanduser(cfg.charts_dir)) 116 | if user == None: 117 | user = cfg.hg_user if cfg.hg_user != '' else None 118 | pswd = cfg.hg_pswd if cfg.hg_pswd != '' else None 119 | src = make_dest_url(src, user, pswd) 120 | os.system('hg clone %s %s' % (src, dest)) 121 | 122 | 123 | def make_dest_url(dest, user, pswd): 124 | """Get the destination Url for pull/push. 125 | 126 | :type dest: str 127 | :type user: str 128 | :type pswd: str 129 | :rtype: str 130 | """ 131 | if user != None: 132 | ret = user 133 | if pswd != None: 134 | ret += ':%s' % pswd 135 | proto, col = dest.split('://') 136 | ret = '%s://%s@%s' % (proto, ret, col) 137 | return ret 138 | else: 139 | return dest 140 | 141 | 142 | def pull(src=None, dest=None, user=None, pswd=None): 143 | """Pull changes from distant repo. 144 | 145 | :type src: str or None 146 | :type dest: str or None 147 | :type user: str or None 148 | :type pswd: str or None 149 | """ 150 | if src == None: 151 | src = cfg.hg_repo 152 | if dest == None: 153 | dest = os.path.abspath(os.path.expanduser(cfg.charts_dir)) 154 | if user == None: 155 | user = cfg.hg_user if cfg.hg_user != '' else None 156 | pswd = cfg.hg_pswd if cfg.hg_pswd != '' else None 157 | src = make_dest_url(src, user, pswd) 158 | os.system('hg pull -u -R %s %s' % (dest, src)) 159 | 160 | 161 | def push(src=None, dest=None, user=None, pswd=None): 162 | """Push changes to distant repo. 163 | 164 | :type src: str or None 165 | :type dest: str or None 166 | :type user: str or None 167 | :type pswd: str or None 168 | """ 169 | if src == None: 170 | src = os.path.abspath(os.path.expanduser(cfg.charts_dir)) 171 | if dest == None: 172 | dest = cfg.hg_repo 173 | if user == None: 174 | user = cfg.hg_user if cfg.hg_user != '' else None 175 | pswd = cfg.hg_pswd if cfg.hg_pswd != '' else None 176 | dest = make_dest_url(dest, user, pswd) 177 | os.system('hg push -R %s %s' % (src, dest)) 178 | 179 | 180 | 181 | # End. 182 | -------------------------------------------------------------------------------- /oroboros/core/cfg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Configuration options. 6 | 7 | Options are automaticly loaded at module import time. 8 | Each is set at module's global scope. 9 | 10 | Here is the list of user's options: 11 | 12 | - username -> user's name/nickname (str) 13 | - usermail -> user's email (str) 14 | - language -> user's language, for gui (str) 15 | - charts_dir -> directory where charts are stored (str) 16 | - use_docutils -> format strings with reStructured Text when possible (bool) 17 | - use_hg -> try to pull on startup and push on shutdown, using hg (bool) 18 | - hg_repo -> distant hg repo url (str) 19 | - hg_user -> hg username (str) 20 | - hg_pswd -> hg password (str) 21 | 22 | For the default chart (here and now chart): 23 | 24 | - dft_name (str) 25 | - dft_location (str) 26 | - dft_country (str) 27 | - dft_zoneinfo (str) 28 | - dft_timezone (timezone.TimeZone) 29 | - dft_latitude (geocoords.Latitude) 30 | - dft_longitude (geocoords.Longitude) 31 | - dft_altitude (geocoords.Altitude) 32 | - dft_comment (str) 33 | - dft_filter (filters.Filter) 34 | 35 | Still unused: 36 | 37 | - atlas_db -> path to sqlite atlas database (str) 38 | 39 | Load config options: 40 | 41 | >>> load() 42 | 43 | Get config options: 44 | 45 | >>> print(username) 46 | John Smith 47 | >>> print(atlas_db) 48 | ~/.oroboros/atlas.db 49 | 50 | Save config options: 51 | 52 | >>> save() 53 | 54 | """ 55 | 56 | from oroboros.core import db 57 | from oroboros.core import geocoords 58 | from oroboros.core import timezone 59 | from oroboros.core.filters import Filter 60 | 61 | import pytz 62 | 63 | 64 | __all__ = ['load', 'save', 65 | 'username', 'usermail', 'language', 'atlas_db', 'use_docutils', 66 | 'use_hg', 'hg_repo', 'hg_user', 'hg_pswd', 67 | 'dft_name', 'dft_location', 'dft_country', 'dft_zoneinfo', 'dft_timezone', 68 | 'dft_latitude', 'dft_longitude', 'dft_altitude', 'dft_comment', 69 | 'dft_filter'] 70 | 71 | 72 | # config options 73 | 74 | username = 'John Smith' 75 | usermail = 'johnsmith@nsa.gov' 76 | language = '' 77 | atlas_db = '~/.oroboros/atlas.db' 78 | charts_dir = '~' 79 | 80 | use_docutils = False 81 | use_hg = False 82 | hg_repo = 'http://hg.atarax.org/public' 83 | hg_user = 'anonymous' 84 | hg_pswd = 'password' 85 | 86 | dft_name = 'Here-Now' 87 | dft_location = 'Lausanne' 88 | dft_country = 'Switzerland' 89 | dft_zoneinfo = 'Europe/Zurich' 90 | dft_timezone = timezone.get('UTC+1') 91 | dft_latitude = geocoords.Latitude(46, 'N', 32) 92 | dft_longitude = geocoords.Longitude(6, 'E', 40) 93 | dft_altitude = geocoords.Altitude(400) 94 | dft_comment = '' 95 | dft_filter = Filter() 96 | 97 | 98 | 99 | def load(): 100 | """Load configuration options from database (set globals).""" 101 | global username, usermail, language, atlas_db, charts_dir 102 | global use_docutils, use_hg, hg_repo, hg_user, hg_pswd 103 | global dft_name, dft_location, dft_country, dft_zoneinfo 104 | global dft_timezone, dft_latitude, dft_longitude, dft_altitude 105 | global dft_comment, dft_filter 106 | # load 107 | sql = "select * from Config;" 108 | res = db.execute(sql).fetchone() 109 | # set options 110 | username = res[0] 111 | usermail = res[1] 112 | language = res[2] 113 | atlas_db = res[3] 114 | charts_dir = res[4] 115 | use_docutils = bool(res[5]) 116 | use_hg = bool(res[6]) 117 | hg_repo = res[7] 118 | hg_user = res[8] 119 | hg_pswd = res[9] 120 | dft_name = res[10] 121 | dft_location = res[11] 122 | dft_country = res[12] 123 | if res[13] not in pytz.all_timezones: 124 | raise ValueError('Invalid timezone %s.' % res[13]) 125 | else: 126 | dft_zoneinfo = res[13] 127 | dft_timezone = timezone.get(res[14]) if res[14] != '' else None 128 | dft_latitude = geocoords.Latitude(*res[15].split(':')) 129 | dft_longitude = geocoords.Longitude(*res[16].split(':')) 130 | dft_altitude = geocoords.Altitude(int(res[17])) 131 | dft_comment = res[18] 132 | dft_filter = Filter(int(res[19])) 133 | 134 | 135 | def save(): 136 | """Save configuration options (globals) in database.""" 137 | global username, usermail, language, atlas_db, charts_dir, dft_location 138 | global use_docutils, use_hg, hg_repo, hg_user, hg_pswd 139 | global dft_country, dft_zoneinfo, dft_timezone, dft_latitude 140 | global dft_longitude, dft_altitude, dft_filter 141 | # save 142 | sql = """update Config set username = ?, usermail = ?, language = ?, 143 | atlas_db = ?, charts_dir = ?, use_docutils = ?, use_hg = ?, hg_repo = ?, 144 | hg_user = ?, hg_pswd = ?, dft_location = ?, dft_country = ?, 145 | dft_zoneinfo = ?, dft_timezone = ?, dft_latitude = ?, dft_longitude = ?, 146 | dft_altitude = ?, dft_filter = ?;""" 147 | var = (username, usermail, language, atlas_db, charts_dir, 148 | int(use_docutils), int(use_hg), hg_repo, hg_user, hg_pswd, 149 | dft_location, dft_country, dft_zoneinfo, 150 | dft_timezone.utc if dft_timezone != None else '', str(dft_latitude), 151 | str(dft_longitude), dft_altitude, dft_filter._idx_) 152 | db.execute(sql, var) 153 | 154 | 155 | 156 | def _test(): 157 | import doctest 158 | doctest.testmod() 159 | 160 | 161 | if __name__ == "__main__": 162 | _test() 163 | else: # get config options 164 | load() 165 | 166 | # End. 167 | -------------------------------------------------------------------------------- /oroboros/gui/aspfilterdialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | New/edit aspects filters. 6 | 7 | """ 8 | 9 | import sys 10 | import os.path 11 | 12 | from PyQt4.QtCore import * 13 | from PyQt4.QtGui import * 14 | 15 | from oroboros.core import cfg 16 | from oroboros.core.filters import all_filters_names 17 | from oroboros.core.aspectsfilters import AspectsFilter 18 | 19 | from oroboros.gui import app 20 | from oroboros.gui import names 21 | 22 | 23 | __all__ = ['AspectsFilterDialog'] 24 | 25 | 26 | _baseDir = os.path.dirname(os.path.abspath(__file__)) 27 | 28 | 29 | class AspectsFilterDialog(QDialog): 30 | 31 | def __init__(self, parent=None, filt=None): 32 | QDialog.__init__(self, parent) 33 | self._parent = parent 34 | tr = self.tr 35 | if isinstance(filt, AspectsFilter): 36 | self._filt = filt 37 | title = unicode(tr('Edit Aspects Filter \xab %(filter)s \xbb')) % { 38 | 'filter': filt._name} 39 | else: 40 | self._filt = AspectsFilter(cfg.dft_filter._aspects._idx_) 41 | self._filt._idx_ = None # copy dft filter and set idx to None 42 | title = tr('New Aspects Filter') 43 | self.setWindowTitle(title) 44 | # size 45 | self.setMinimumWidth(350) 46 | self.setMinimumHeight(320) 47 | self.setSizeGripEnabled(True) 48 | # layout 49 | grid = QGridLayout(self) 50 | self.setLayout(grid) 51 | # filter name 52 | grid.addWidget(QLabel(tr('Filter name')), 0, 0) 53 | self.nameEdit = QLineEdit(self) 54 | grid.addWidget(self.nameEdit, 0, 1) 55 | # tab widget 56 | tabs = QTabWidget(self) 57 | grid.addWidget(tabs, 1, 0, 1, 2) 58 | self._cb = dict() ## holds the checkboxes 59 | 60 | # ### main aspects ### 61 | mainWidget = QWidget() 62 | tabs.addTab(mainWidget, tr('Main', 'Main aspects')) 63 | mainGrid = QGridLayout() 64 | mainWidget.setLayout(mainGrid) 65 | x = (0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3) 66 | y = (0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3) 67 | mainAspects = ('Conjunction', 'Opposition', 'Trine', 'Square', 'Sextile', 68 | 'Quincunx', 'SesquiSquare', 'SemiSquare', 'SemiSextile', 'SquiSquare', 69 | 'SquiSextile', 'Quintile', 'BiQuintile', 'SemiQuintile') 70 | for i, asp in enumerate(mainAspects): 71 | self._cb[asp] = QCheckBox(names.aspects[asp], self) 72 | mainGrid.addWidget(self._cb[asp], x[i], y[i]) 73 | 74 | # ### other aspects ###$ 75 | otherWidget = QWidget() 76 | tabs.addTab(otherWidget, tr('Others', 'Other aspects')) 77 | otherGrid = QGridLayout() 78 | otherWidget.setLayout(otherGrid) 79 | x = (0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3) 80 | y = (0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3) 81 | otherAspects = ('Novile', 'BiNovile', 'QuadriNovile', 'SemiNovile', 82 | 'Septile', 'BiSeptile', 'TriSeptile', 'Undecile', 'BiUndecile', 83 | 'TriUndecile', 'QuadUndecile', 'QuinUndecile') 84 | for i, asp in enumerate(otherAspects): 85 | self._cb[asp] = QCheckBox(names.aspects[asp], self) 86 | otherGrid.addWidget(self._cb[asp], x[i], y[i]) 87 | 88 | # comment 89 | grid.addWidget(QLabel(tr('Comment')), 2, 0, Qt.AlignTop) 90 | self.commentEdit = QTextEdit('', self) 91 | grid.addWidget(self.commentEdit, 2, 1) 92 | # buttons 93 | buttonsLayout = QHBoxLayout() 94 | resetButton = QPushButton(tr('Reset'), self) 95 | self.connect(resetButton, SIGNAL('clicked()'), self.reset) 96 | buttonsLayout.addWidget(resetButton) 97 | cancelButton = QPushButton(tr('Cancel'), self) 98 | self.connect(cancelButton, SIGNAL('clicked()'), self.reject) 99 | buttonsLayout.addWidget(cancelButton) 100 | okButton = QPushButton(tr('Ok'), self) 101 | okButton.setDefault(True) 102 | self.connect(okButton, SIGNAL('clicked()'), self.accept) 103 | buttonsLayout.addWidget(okButton) 104 | grid.addLayout(buttonsLayout, 3, 0, 1, 2) 105 | # load entries 106 | self.reset() 107 | # resize 108 | self.resize(550, 300) 109 | 110 | def reset(self): 111 | """Reset entries.""" 112 | # name 113 | self.nameEdit.setText(self._filt._name) 114 | # comment 115 | self.commentEdit.setPlainText(self._filt._comment) 116 | # values 117 | for asp, val in self._filt.items(): 118 | self._cb[asp].setChecked(val) 119 | 120 | def accept(self): 121 | """Set filter new values and save.""" 122 | tr = self.tr 123 | # name 124 | name = unicode(self.nameEdit.text()) 125 | if name == '': 126 | QMessageBox.critical(self, tr('Missing Name'), 127 | tr('Please set filter name.')) 128 | self.nameEdit.setFocus() 129 | return 130 | # comment 131 | cmt = unicode(self.commentEdit.toPlainText()) 132 | # set values 133 | self._filt.set(name=name, comment=cmt) 134 | for asp, cb in self._cb.items(): 135 | self._filt[asp] = cb.isChecked() 136 | # save 137 | try: 138 | self._filt.save() 139 | except ValueError: # duplicate filter 140 | QMessageBox.critical(self, tr('Error'), 141 | unicode(tr('Duplicate filter name \xab %s \xbb.')) % self._filt._name) 142 | self.nameEdit.setFocus() 143 | return 144 | # reload cfg in case filter is default, 145 | # and opened charts if using this filter 146 | if __name__ != '__main__': 147 | app.aspectsFilterUpdatedEvent(self._filt._idx_) 148 | # done 149 | self.done(QDialog.Accepted) 150 | 151 | 152 | 153 | def main(): 154 | app = QApplication(sys.argv) 155 | main = AspectsFilterDialog() 156 | main.show() 157 | sys.exit(app.exec_()) 158 | 159 | 160 | if __name__ == '__main__': 161 | main() 162 | 163 | # End. 164 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """*Oroboros* is an astrology software written in Python. 5 | It is based on the following modules: 6 | 7 | - AstroDienst's Swiss Ephemeris (pyswisseph_) version >= 1.74.00-0 8 | - Olson timezones (pytz_) 9 | - Qt toolkit (PyQt4_) version >= 4.3 10 | 11 | Optionally: 12 | 13 | - Docutils_ version = 0.5 14 | - Mercurial_ (for distant charts repositories) 15 | - Supybot_ (for IRC plugin) 16 | 17 | =========== 18 | What's new? 19 | =========== 20 | 21 | - Usual bug fixes 22 | - Additional asteroids: Eris, Sedna, Quaoar, Nessus, Varuna 23 | 24 | ======== 25 | Features 26 | ======== 27 | 28 | - Charts: natal/radix, transits, progression, harmonics, profection 29 | - Open Astrolog or Skylendar charts too 30 | - Save charts as images in various formats (png, jpg, bmp, svg,...) 31 | - Query the online GeoNames.org_ atlas database 32 | - Calculates all planets, nodes 33 | - More than 300 fixed stars 34 | - Asteroids (Eris, Quaoar, Sedna, etc) 35 | - Uses Swiss, JPL, or Moshier Ephemeris 36 | - A flexible and powerfull configuration system 37 | - Geocentric, topocentric, heliocentric or barycentric charts 38 | - Various house systems, including Gauquelin sectors 39 | - Tropical or sidereal zodiacs 40 | - Midpoints calculations and drawings 41 | - Format comments with Docutils_ reStructured Text 42 | - Synchronize your charts with a distant repository (using Mercurial_) 43 | - A few toys for IRC chat addicts (Supybot_ plugin) 44 | - And easy extension capabilities with the Python language... 45 | 46 | .. _pyswisseph: http://pypi.python.org/pypi/pyswisseph 47 | .. _pytz: http://pytz.sourceforge.net 48 | .. _PyQt4: http://www.riverbankcomputing.co.uk/pyqt 49 | .. _Docutils: http://docutils.sourceforge.net 50 | .. _Mercurial: http://www.selenic.com/mercurial 51 | .. _Supybot: http://supybot.com 52 | .. _GeoNames.org: http://www.geonames.org 53 | 54 | =============== 55 | Oroboros manual 56 | =============== 57 | 58 | Installation 59 | ------------ 60 | Download and install the package and the required modules (most of them 61 | are available in your distro repositories). For example, uncompress the latest 62 | source package of *Oroboros* with ``tar jxf oroboros-xxxxxxxx.tar.bz2``, then 63 | ``cd oroboros-xxxxxxxx``, finally (with super-user permission) type ``python 64 | setup.py install``. 65 | 66 | Ephemeris files 67 | ~~~~~~~~~~~~~~~ 68 | You should also install the compressed ephemeris data files on your system 69 | (``/usr/local/share/swisseph`` looks like a good idea). These files are freely 70 | distributed on `AstroDienst public FTP server`_. 71 | 72 | .. _`AstroDienst public FTP server`: ftp://ftp.astro.com/pub/swisseph/ephe 73 | 74 | A simple installation would require that you download the following files, 75 | allowing calculations over the period from 1800 to 2399: 76 | 77 | - ``sepl_18.se1`` 78 | - ``semo_18.se1`` 79 | - ``seas_18.se1`` 80 | - ``fixstars.cat`` 81 | - ``seorbel.txt`` 82 | 83 | You can also grab the following asteroids files (other asteroids can be 84 | implemented on demand): 85 | 86 | - ``136199 Eris`` 87 | - ``7066 Nessus`` 88 | - ``50000 Quaoar`` 89 | - ``90377 Sedna`` 90 | - ``20000 Varuna`` 91 | - ``128 Nemesis`` 92 | 93 | Usage 94 | ----- 95 | If everything goes well you will be able to start the application by typing 96 | ``oroboros`` in your shell. 97 | 98 | First steps 99 | ~~~~~~~~~~~ 100 | Update the configuration settings, especially the path to ephemeris files. 101 | You may also create an aspects filter and an orbs filter for the midpoints 102 | settings, otherwise you'll have some surprises. 103 | 104 | ======== 105 | Download 106 | ======== 107 | """ 108 | 109 | import sys, os 110 | from distutils.core import setup 111 | 112 | VERSION = '20080712' 113 | 114 | 115 | setup( 116 | # meta info 117 | name = 'oroboros', 118 | version = VERSION, 119 | description = 'Astrology software', 120 | long_description = __doc__, 121 | author = 'S.Marquis', 122 | author_email = 'stnsls@gmail.com', 123 | #url = 'http://oroboros.atarax.org', 124 | download_url = 'http://pypi.python.org/pypi/oroboros', 125 | classifiers = [ 126 | 'Development Status :: 3 - Alpha', 127 | 'Environment :: X11 Applications :: Qt', 128 | 'Intended Audience :: Religion', 129 | 'Intended Audience :: Science/Research', 130 | 'License :: OSI Approved :: GNU General Public License (GPL)', 131 | 'Topic :: Religion', 132 | 'Topic :: Scientific/Engineering :: Astronomy' 133 | ], 134 | keywords = 'Astrology Ephemeris Swisseph', 135 | # files 136 | packages = [ 137 | 'oroboros', 138 | 'oroboros.core', 139 | 'oroboros.cli', 140 | 'oroboros.gui', 141 | 'oroboros.irc' 142 | ], 143 | package_data = { 144 | 'oroboros': ['*.txt'], 145 | 'oroboros.core': [ 146 | '*.sql', 147 | '*.txt' 148 | ], 149 | 'oroboros.gui': [ 150 | 'icons/*.png', 151 | 'icons/zodiac/*.png', 152 | 'icons/cusps/*.png', 153 | 'icons/planets/*.png', 154 | 'tr/*.qm', 155 | 'tr/*.ts' 156 | ], 157 | 'oroboros.irc': ['*.txt'] 158 | }, 159 | scripts = [ 160 | 'bin/oroboros', 161 | 'bin/oroboros-hg' 162 | ], 163 | data_files = [ 164 | ('/usr/share/applications', ['share/oroboros.desktop']), 165 | ('/usr/share/pixmaps', ['share/oroboros.png']) 166 | ] 167 | ) 168 | 169 | # post install 170 | if 'install' in sys.argv: 171 | try: 172 | os.system('update-desktop-database') 173 | except: 174 | pass 175 | 176 | 177 | # End. 178 | -------------------------------------------------------------------------------- /oroboros/gui/orbfilterdialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | New/edit orbs filters. 6 | 7 | """ 8 | 9 | import sys 10 | import os.path 11 | 12 | from PyQt4.QtCore import * 13 | from PyQt4.QtGui import * 14 | 15 | from oroboros.core import cfg 16 | from oroboros.core.orbsfilters import OrbsFilter 17 | 18 | from oroboros.gui import app 19 | from oroboros.gui import names 20 | 21 | 22 | __all__ = ['OrbsFilterDialog'] 23 | 24 | 25 | _baseDir = os.path.dirname(os.path.abspath(__file__)) 26 | 27 | 28 | 29 | class OrbsFilterDialog(QDialog): 30 | 31 | def __init__(self, parent=None, filt=None): 32 | QDialog.__init__(self, parent) 33 | self._parent = parent 34 | tr = self.tr 35 | if isinstance(filt, OrbsFilter): 36 | self._filt = filt 37 | title = unicode(tr('Edit Orbs Filter \xab %(filter)s \xbb')) % { 38 | 'filter': filt._name} 39 | else: 40 | self._filt = OrbsFilter(cfg.dft_filter._orbs._idx_) 41 | self._filt._idx_ = None # copy dft filter and set idx to None 42 | title = tr('New Orbs Filter') 43 | self.setWindowTitle(title) 44 | # size 45 | self.setMinimumWidth(500) 46 | self.setMinimumHeight(320) 47 | self.setSizeGripEnabled(True) 48 | # layout 49 | grid = QGridLayout(self) 50 | self.setLayout(grid) 51 | # filter name 52 | grid.addWidget(QLabel(tr('Filter name')), 0, 0) 53 | self.nameEdit = QLineEdit(self) 54 | grid.addWidget(self.nameEdit, 0, 1) 55 | # tab widget 56 | tabs = QTabWidget(self) 57 | grid.addWidget(tabs, 1, 0, 1, 2) 58 | self._sb = dict() ## holds the spinboxes 59 | 60 | # ### main aspects ### 61 | mainWidget = QWidget() 62 | tabs.addTab(mainWidget, tr('Main', 'Main aspects')) 63 | mainGrid = QGridLayout() 64 | mainWidget.setLayout(mainGrid) 65 | x = (0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3) 66 | y = (0,2,4,6,0,2,4,6,0,2,4,6,0,2,4,6) 67 | mainAspects = ('Conjunction', 'Opposition', 'Trine', 'Square', 'Sextile', 68 | 'Quincunx', 'SesquiSquare', 'SemiSquare', 'SemiSextile', 'SquiSquare', 69 | 'SquiSextile', 'Quintile', 'BiQuintile', 'SemiQuintile') 70 | for i, asp in enumerate(mainAspects): 71 | mainGrid.addWidget(QLabel(names.aspects[asp]), x[i], y[i]) 72 | self._sb[asp] = QDoubleSpinBox(self) 73 | self._sb[asp].setRange(0, 30) 74 | self._sb[asp].setButtonSymbols(QAbstractSpinBox.PlusMinus) 75 | self._sb[asp].setSuffix(tr('\xb0', 'Degrees')) 76 | mainGrid.addWidget(self._sb[asp], x[i], y[i]+1) 77 | 78 | # ### other aspects ### 79 | otherWidget = QWidget() 80 | tabs.addTab(otherWidget, tr('Others', 'Other aspects')) 81 | otherGrid = QGridLayout() 82 | otherWidget.setLayout(otherGrid) 83 | x = (0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3) 84 | y = (0,2,4,6,0,2,4,6,0,2,4,6,0,2,4,6) 85 | otherAspects = ('Novile', 'BiNovile', 'QuadriNovile', 'SemiNovile', 86 | 'Septile', 'BiSeptile', 'TriSeptile', 'Undecile', 'BiUndecile', 87 | 'TriUndecile', 'QuadUndecile', 'QuinUndecile') 88 | for i, asp in enumerate(otherAspects): 89 | otherGrid.addWidget(QLabel(names.aspects[asp]), x[i], y[i]) 90 | self._sb[asp] = QDoubleSpinBox(self) 91 | self._sb[asp].setRange(0, 30) 92 | self._sb[asp].setButtonSymbols(QAbstractSpinBox.PlusMinus) 93 | self._sb[asp].setSuffix(tr('\xb0', 'Degrees')) 94 | otherGrid.addWidget(self._sb[asp], x[i], y[i]+1) 95 | 96 | # comment 97 | grid.addWidget(QLabel(tr('Comment')), 2, 0, Qt.AlignTop) 98 | self.commentEdit = QTextEdit('', self) 99 | grid.addWidget(self.commentEdit, 2, 1) 100 | # buttons 101 | buttonsLayout = QHBoxLayout() 102 | resetButton = QPushButton(tr('Reset'), self) 103 | self.connect(resetButton, SIGNAL('clicked()'), self.reset) 104 | buttonsLayout.addWidget(resetButton) 105 | cancelButton = QPushButton(tr('Cancel'), self) 106 | self.connect(cancelButton, SIGNAL('clicked()'), self.reject) 107 | buttonsLayout.addWidget(cancelButton) 108 | okButton = QPushButton(tr('Ok'), self) 109 | okButton.setDefault(True) 110 | self.connect(okButton, SIGNAL('clicked()'), self.accept) 111 | buttonsLayout.addWidget(okButton) 112 | grid.addLayout(buttonsLayout, 3, 0, 1, 2) 113 | # load entries 114 | self.reset() 115 | # resize 116 | self.resize(550, 300) 117 | 118 | def reset(self): 119 | """Reset entries.""" 120 | # name 121 | self.nameEdit.setText(self._filt._name) 122 | # comment 123 | self.commentEdit.setPlainText(self._filt._comment) 124 | # values 125 | for asp, val in self._filt.items(): 126 | self._sb[asp].setValue(val) 127 | 128 | def accept(self): 129 | """Set filter new values and save.""" 130 | tr = self.tr 131 | # name 132 | name = unicode(self.nameEdit.text()) 133 | if name == '': 134 | QMessageBox.critical(self, tr('Missing Name'), 135 | tr('Please set filter name.')) 136 | self.nameEdit.setFocus() 137 | return 138 | # comment 139 | cmt = unicode(self.commentEdit.toPlainText()) 140 | # set values 141 | self._filt.set(name=name, comment=cmt) 142 | for asp, sb in self._sb.items(): 143 | self._filt[asp] = sb.value() 144 | # save 145 | try: 146 | self._filt.save() 147 | except ValueError: # duplicate filter 148 | QMessageBox.critical(self, tr('Error'), 149 | unicode(tr('Duplicate filter name \xab %s \xbb.')) % self._filt._name) 150 | self.nameEdit.setFocus() 151 | return 152 | # reload cfg in case filter is default, 153 | # and opened charts if using this filter 154 | if __name__ != '__main__': 155 | app.orbsFilterUpdatedEvent(self._filt._idx_) 156 | # done 157 | self.done(QDialog.Accepted) 158 | 159 | 160 | 161 | def main(): 162 | app = QApplication(sys.argv) 163 | main = OrbsFilterDialog() 164 | main.show() 165 | sys.exit(app.exec_()) 166 | 167 | 168 | if __name__ == '__main__': 169 | main() 170 | 171 | # End. 172 | -------------------------------------------------------------------------------- /oroboros/core/etc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Configuration file. 6 | 7 | Users have a configuration file loaded on startup, containing the 8 | minimal information: 9 | 10 | - path to sqlite3 database 11 | 12 | The original configuration file (``etc.txt``) should be checked and 13 | modified by the administrator, with correct values. The default values 14 | inside ``etc.py`` must also be checked accordingly. 15 | 16 | This file is copied to user's home by default in a dedicated directory 17 | (``~/.oroboros``) on the first startup, but only if there is no config in the 18 | system-wide configuration path (``/etc/oroboros.conf`` under linux) or in the 19 | current working directory (``./oroboros.conf``). All these names can easily be 20 | changed inside the source code. 21 | 22 | Importation of the module does read (and create) the config file. 23 | 24 | Copy original config file to home: 25 | 26 | >>> create() # config already installed: should return False 27 | False 28 | 29 | Read your config and set module globals (done on explicit import): 30 | 31 | >>> read() 32 | True 33 | 34 | Get your config values: 35 | 36 | >>> sqlite['path'] 37 | '~/.oroboros/oroboros.db' 38 | 39 | """ 40 | 41 | import os.path 42 | import shutil 43 | import stat 44 | import ConfigParser 45 | 46 | 47 | __all__ = ['read', 'exists', 'create', 'sqlite'] 48 | 49 | 50 | # -- CONFIGURATION -- default values: 51 | # Dont forget to modify etc.txt. 52 | # These values are used only if there is no config file to read anywhere. 53 | 54 | #: Sqlite config section. 55 | #: :type sqlite: dict 56 | sqlite = { 57 | 'path': '~/.oroboros/oroboros.db' 58 | } 59 | 60 | 61 | # Paths: 62 | # There should be no reason to change them unless you know what you are doing. 63 | 64 | #: Configuration file name. 65 | #: :type _cfgname: str 66 | _cfgname = 'oroboros.conf' 67 | 68 | #: User's configuration directory. 69 | #: If empty str or None, don't create this directory. 70 | #: :type _dir: str 71 | _dir = '~/.oroboros' 72 | 73 | #: User's default config file path. 74 | #: :type _path: str 75 | _path = os.path.join(_dir, _cfgname) 76 | 77 | #: System-wide configuration. 78 | #: :type _sysconf: str 79 | _sysconf = os.path.join('/etc', _cfgname) 80 | 81 | # current working dir path 82 | _baseconf = os.path.join('./', _cfgname) 83 | 84 | 85 | # -- END CONFIGURATION -- Don't modify below! 86 | 87 | 88 | #: Configuration file exist flag. 89 | #: :type exists: bool 90 | exists = False 91 | 92 | #: Original configuration file. 93 | #: :type original_file: str 94 | original_file = 'etc.txt' 95 | 96 | 97 | def read(path=_path, force=False): 98 | """Read configuration and set globals. 99 | 100 | If user has no personal config, try to read system's config, 101 | or current directory config. 102 | Return True if something has been read; or False if nothing. 103 | Set force=True if you want to re-read config file. 104 | 105 | :type path: str 106 | :type force: bool 107 | :rtype: bool 108 | """ 109 | # dont re-read config unless forcing 110 | global exists 111 | if not force: 112 | if exists == True: 113 | return True 114 | # try to read user's config 115 | ini = ConfigParser.SafeConfigParser() 116 | c = ini.read(os.path.expanduser(path)) 117 | if len(c) != 0: 118 | return _set_globals(ini) 119 | # try to read system config 120 | global _sysconf 121 | ini = ConfigParser.SafeConfigParser() 122 | c = ini.read(_sysconf) 123 | if len(c) != 0: 124 | return _set_globals(ini) 125 | # try to read current dir config 126 | global _baseconf 127 | ini = ConfigParser.SafeConfigParser() 128 | c = ini.read(_baseconf) 129 | if len(c) != 0: 130 | return _set_globals(ini) 131 | # using default values... 132 | return False 133 | 134 | 135 | def _set_globals(parser): 136 | """Set configuration globals using a config parser. 137 | 138 | By the way, sets the ``exists`` flag to True. 139 | 140 | :type parser: ConfigParser 141 | :rtype: bool 142 | """ 143 | global exists, sqlite 144 | if parser.has_option('sqlite', 'path'): 145 | sqlite['path'] = parser.get('sqlite', 'path') 146 | exists = True 147 | return True 148 | 149 | 150 | def create(path=_path, create_dir=_dir): 151 | """Copy original config file to user's home. 152 | 153 | If needed, try to create a dedicated directory. 154 | Return True; or False if something already exists. 155 | 156 | :type path: str 157 | :type create_dir: str 158 | :rtype: bool 159 | """ 160 | path = os.path.expanduser(path) 161 | # check if file already exists 162 | if (os.path.exists(path)): 163 | return False 164 | # check if dir exists 165 | if create_dir not in (None, ''): 166 | if not _create_dir(create_dir): 167 | return False 168 | # copy file 169 | src = os.path.join(os.path.abspath(os.path.dirname(__file__)), 170 | original_file) 171 | shutil.copyfile(src, path) 172 | ## chown and chmod file 173 | os.chown(path, os.getuid(), os.getgid()) 174 | os.chmod(path, stat.S_IRUSR|stat.S_IWUSR) 175 | return True 176 | 177 | 178 | def _create_dir(path=_dir): 179 | """Create the personal oroboros directory (``~/.oroboros``). 180 | 181 | Return True; or False if something else than a directory already exists. 182 | 183 | :rtype: bool 184 | """ 185 | path = os.path.expanduser(path) 186 | if (os.path.exists(path)): 187 | if not (os.path.isdir(path)): 188 | return False 189 | else: 190 | return True 191 | else: 192 | os.mkdir(path) 193 | os.chmod(path, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR) 194 | return True 195 | 196 | 197 | def _test(): 198 | import doctest 199 | doctest.testmod() 200 | 201 | 202 | if __name__ == "__main__": 203 | _test() 204 | else: # read config file on module import, or try to create it 205 | if not read(): 206 | create() 207 | read(force=True) 208 | 209 | # End. 210 | -------------------------------------------------------------------------------- /oroboros/core/db.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | SQLite database system. 6 | 7 | Connect to database, mention your db path: 8 | 9 | >>> connect(':memory:') 10 | 11 | Connect to database using default path: 12 | 13 | >>> connect() 14 | 15 | Now a cursor has been created. You can send queries using it transparently. 16 | 17 | >>> print execute("select 1").fetchall() 18 | [(1,)] 19 | 20 | Close connection: 21 | 22 | >>> close() 23 | 24 | """ 25 | 26 | import os.path 27 | import re 28 | import stat 29 | import sqlite3 as sqlite 30 | 31 | import oroboros 32 | from oroboros.core import etc 33 | 34 | 35 | __all__ = ['Object', 36 | 'connect', 'execute', 'close', 'autoconnect', 37 | 'install', 'connect_atlas', 'install_atlas'] 38 | 39 | # default db path 40 | _dsn = etc.sqlite['path'] 41 | 42 | #: Connection resource singleton 43 | _cnx = None 44 | 45 | #: Cursor singleton 46 | _cur = None 47 | 48 | ## oroboros.db._atl_cnx 49 | ##_atl_cnx = None # atlas connection 50 | 51 | ## oroboros.db._atl_cur 52 | ##_atl_cur = None # atlas cursor 53 | 54 | 55 | _basedir = os.path.abspath(os.path.dirname(__file__)) 56 | 57 | 58 | class Object(object): 59 | """Base class for all objects stored in database.""" 60 | 61 | __slots__ = ['_idx_'] 62 | 63 | def _get_idx(self): 64 | """Return db object idx. 65 | 66 | :rtype: int 67 | """ 68 | return self._idx_ 69 | 70 | def _set_idx(self, idx): 71 | """Set db object idx. 72 | 73 | :type idx: int > 0 74 | :raise ValueError: invalid idx 75 | """ 76 | if idx == None: 77 | self._idx_ = None 78 | else: 79 | idx = int(idx) 80 | if idx < 1: 81 | raise ValueError('Object %s Db index %s < 0.' % (self, idx)) 82 | else: 83 | self._idx_ = idx 84 | 85 | 86 | # Database functions 87 | 88 | def _connect(dsn=_dsn): 89 | """Connect to database. 90 | 91 | :type dsn: str 92 | :rtype: connection resource 93 | """ 94 | dsn = os.path.expanduser(dsn) 95 | return sqlite.connect(dsn, isolation_level=None, 96 | detect_types=sqlite.PARSE_DECLTYPES) 97 | 98 | 99 | def _cursor(conn): 100 | """Create a cursor. 101 | 102 | :rtype: cursor 103 | """ 104 | return conn.cursor() 105 | 106 | 107 | def connect(dsn=_dsn): 108 | """Connect to database, and create a cursor. 109 | 110 | Connection and cursor are set globally. 111 | 112 | :see: ``path`` option in oroboros.conf file. 113 | 114 | :type dsn: str 115 | """ 116 | dsn = os.path.expanduser(dsn) 117 | cnx = _connect(dsn) 118 | cnx = _create_functions(cnx) # user-defined functions 119 | cur = _cursor(cnx) 120 | global _cnx, _cur 121 | _cnx, _cur = cnx, cur 122 | 123 | 124 | ## oroboros.db.connect_atlas 125 | ##def connect_atlas(dsn): 126 | ## """Connect to atlas database. 127 | ## 128 | ## See the 'atlas_db' config option. 129 | ## Default behavior is store connection and cursor globally. 130 | ## If return_conn is not False, return connection and cursor in a tuple. 131 | ## 132 | ## """ 133 | ## dsn = os.path.expanduser(dsn) 134 | ## cnx = _connect(dsn) 135 | ## cnx = _create_functions(cnx) # user-defined functions 136 | ## cur = _cursor(cnx) 137 | ## global _atl_cnx, _atl_cur 138 | ## _atl_cnx, _atl_cur = cnx, cur 139 | 140 | 141 | def execute(sql, var=None): 142 | """Execute a SQL query. 143 | 144 | :type sql: str 145 | :type var: sequence 146 | :rtype: cursor 147 | """ 148 | global _cur 149 | try: 150 | if var == None: 151 | _cur.execute(sql) 152 | else: 153 | _cur.execute(sql, var) 154 | except: 155 | #print(sql, var) 156 | raise 157 | return _cur 158 | 159 | 160 | def close(): 161 | """Close connection.""" 162 | global _cnx, _cur 163 | try: 164 | _cur.execute('vacuum;') 165 | except: 166 | pass 167 | return _cnx.close() 168 | 169 | 170 | def autoconnect(dsn=_dsn): 171 | """Automagic database connection on module import. 172 | 173 | Return True if connected, False if not connected. 174 | 175 | :type dsn: str 176 | :rtype: bool 177 | """ 178 | try: 179 | connect(dsn) 180 | except: 181 | return False 182 | return True 183 | 184 | 185 | def _execute_file(path): 186 | """Execute SQL statements from a file. 187 | 188 | Statements must be delimited by ``;/*End*/``. 189 | 190 | :type path: str 191 | """ 192 | f = open(path, 'r') 193 | lines = f.read().split(';/*End*/') 194 | f.close() 195 | for sql in lines: 196 | execute(sql) 197 | 198 | 199 | def install(path=_dsn): 200 | """First install procedure. 201 | 202 | Return True on success. 203 | Return 1 if install already done (and version up to date). 204 | Return False if unable to query database (not connected). 205 | 206 | Delete the database and recreate it if this is a newer version. 207 | In this case return 2. 208 | 209 | :type path: str 210 | :rtype: bool or int 211 | """ 212 | # check db not exists 213 | sql = "select count(name) from sqlite_master where type = 'table';" 214 | try: 215 | res = execute(sql).fetchone()[0] 216 | except: 217 | return False # not connected 218 | if int(res) > 0: 219 | # stuff in there. check version 220 | if not _check_version(): 221 | close() 222 | os.remove(os.path.expanduser(path)) 223 | autoconnect() 224 | install() 225 | return 2 226 | else: 227 | return 1 228 | # executes 229 | f = os.path.join(_basedir, 'sqlite-creates.sql') 230 | _execute_file(f) 231 | f = os.path.join(_basedir, 'sqlite-inserts.sql') 232 | _execute_file(f) 233 | # chmod db file 234 | os.chmod(os.path.expanduser(path), stat.S_IRUSR|stat.S_IWUSR) 235 | # clean 236 | execute('vacuum;') 237 | return True 238 | 239 | 240 | def _check_version(): 241 | """Return True if db is up to date. 242 | 243 | :rtype: bool 244 | """ 245 | version = execute('select version from Info;') 246 | version = int(version.fetchone()[0]) 247 | if version < int(oroboros.__version__): 248 | return False 249 | else: 250 | return True 251 | 252 | 253 | ## oroboros.db.install_atlas 254 | ##def install_atlas(path): 255 | ## """Create atlas database structure. 256 | ## 257 | ## Return True on success. 258 | ## Return -1 if install already done. 259 | ## Return -2 if unable to query database (not connected). 260 | ## 261 | ## """ 262 | ## # check db not exists 263 | ## sql = "select count(name) from sqlite_master where type = 'table';" 264 | ## try: 265 | ## res = execute(sql, cursor=cursor).fetchone()[0] 266 | ## except: 267 | ## return -2 268 | ## if int(res) > 0: 269 | ## return -1 270 | ## # executes 271 | ## f = os.path.join(_basedir, 'atlas.sql') 272 | ## _execute_file(f) 273 | ## # chmod db file 274 | ## os.chmod(os.path.expanduser(path), stat.S_IRUSR|stat.S_IWUSR) 275 | ## return True 276 | 277 | 278 | def _create_functions(cnx): 279 | """Create user-defined functions. 280 | 281 | :type cnx: connection resource 282 | :rtype: connection resource 283 | """ 284 | # regexp search 285 | def regexp_search(pattern, string): 286 | if re.compile(pattern, re.IGNORECASE).search(string) != None: 287 | return 1 288 | return 0 289 | cnx.create_function('rgxp', 2, regexp_search) 290 | return cnx 291 | 292 | 293 | def _test(): 294 | import doctest 295 | doctest.testmod() 296 | 297 | 298 | if __name__ == "__main__": 299 | _test() 300 | else: # try to connect and create database if necessary 301 | autoconnect() 302 | install() 303 | 304 | # End. 305 | -------------------------------------------------------------------------------- /oroboros/gui/names.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Translated names. 6 | 7 | """ 8 | 9 | from PyQt4.QtCore import * 10 | from PyQt4.QtGui import * 11 | 12 | 13 | __all__ = ['_encoding', 'signs', 'planets', 'houses', 'houseSystems', 14 | 'sidModes', 'objects'] 15 | 16 | 17 | tr = lambda x, y=None: qApp.translate('names.py', x, y) 18 | 19 | 20 | _encoding = unicode(tr('iso-8859-1', 'Encoding (reading non-utf8 files)')) 21 | 22 | 23 | #: Signs 24 | signs = { 25 | 'Ari': unicode(tr('Ari', 'Aries')), 26 | 'Tau': unicode(tr('Tau', 'Taurus')), 27 | 'Gem': unicode(tr('Gem', 'Gemini')), 28 | 'Can': unicode(tr('Can', 'Cancer')), 29 | 'Leo': unicode(tr('Leo', 'Leo')), 30 | 'Vir': unicode(tr('Vir', 'Virgo')), 31 | 'Lib': unicode(tr('Lib', 'Libra')), 32 | 'Sco': unicode(tr('Sco', 'Scorpio')), 33 | 'Sag': unicode(tr('Sag', 'Sagittarius')), 34 | 'Cap': unicode(tr('Cap', 'Capricorn')), 35 | 'Aqu': unicode(tr('Aqu', 'Aquarius')), 36 | 'Pis': unicode(tr('Pis', 'Pisces')) 37 | } 38 | 39 | #: Planets 40 | planets = { 41 | 'Sun': unicode(tr('Sun')), 42 | 'Moon': unicode(tr('Moon')), 43 | 'Mercury': unicode(tr('Mercury')), 44 | 'Venus': unicode(tr('Venus')), 45 | 'Mars': unicode(tr('Mars')), 46 | 'Jupiter': unicode(tr('Jupiter')), 47 | 'Saturn': unicode(tr('Saturn')), 48 | 'Uranus': unicode(tr('Uranus')), 49 | 'Neptune': unicode(tr('Neptune')), 50 | 'Pluto': unicode(tr('Pluto')), 51 | 'Earth': unicode(tr('Earth')), 52 | 'Chiron': unicode(tr('Chiron')), 53 | 'Pholus': unicode(tr('Pholus')), 54 | 'Ceres': unicode(tr('Ceres')), 55 | 'Pallas': unicode(tr('Pallas')), 56 | 'Juno': unicode(tr('Juno')), 57 | 'Vesta': unicode(tr('Vesta')), 58 | 'Rahu (mean)': unicode(tr('Rahu (mean)')), 59 | 'Rahu (true)': unicode(tr('Rahu (true)')), 60 | 'Ketu (mean)': unicode(tr('Ketu (mean)')), 61 | 'Ketu (true)': unicode(tr('Ketu (true)')), 62 | 'Lilith (mean)': unicode(tr('Lilith (mean)')), 63 | 'Lilith (true)': unicode(tr('Lilith (true)')), 64 | 'Priapus (mean)': unicode(tr('Priapus (mean)')), 65 | 'Priapus (true)': unicode(tr('Priapus (true)')), 66 | 'Cupido': unicode(tr('Cupido')), 67 | 'Hades': unicode(tr('Hades')), 68 | 'Zeus': unicode(tr('Zeus')), 69 | 'Kronos': unicode(tr('Kronos')), 70 | 'Apollon': unicode(tr('Apollon')), 71 | 'Admetos': unicode(tr('Admetos')), 72 | 'Vulkanus': unicode(tr('Vulkanus')), 73 | 'Poseidon': unicode(tr('Poseidon')), 74 | 'Isis': unicode(tr('Isis')), 75 | 'Nibiru': unicode(tr('Nibiru')), 76 | 'Harrington': unicode(tr('Harrington')), 77 | 'Neptune (Leverrier)': unicode(tr('Neptune (Leverrier)')), 78 | 'Neptune (Adams)': unicode(tr('Neptune (Adams)')), 79 | 'Pluto (Lowell)': unicode(tr('Pluto (Lowell)')), 80 | 'Pluto (Pickering)': unicode(tr('Pluto (Pickering)')), 81 | 'Vulcan': unicode(tr('Vulcan')), 82 | 'White Moon': unicode(tr('White Moon')), 83 | 'Proserpina': unicode(tr('Proserpina')), 84 | 'Waldemath': unicode(tr('Waldemath')), 85 | 'Asc': unicode(tr('Asc')), 86 | 'Mc': unicode(tr('Mc')), 87 | 'Dsc': unicode(tr('Dsc')), 88 | 'Ic': unicode(tr('Ic')), 89 | 'Armc': unicode(tr('Armc')), 90 | 'Vertex': unicode(tr('Vertex')), 91 | 'Equatorial Ascendant': unicode(tr('Equatorial Ascendant')), 92 | 'Co-ascendant (Koch)': unicode(tr('Co-ascendant (Koch)')), 93 | 'Co-ascendant (Munkasey)': unicode(tr('Co-ascendant (Munkasey)')), 94 | 'Polar Ascendant (Munkasey)': unicode(tr('Polar Ascendant (Munkasey)')), 95 | 'Part of Fortune (Rudhyar)': unicode(tr('Part of Fortune (Rudhyar)')), 96 | '128 Nemesis': unicode(tr('128 Nemesis')) 97 | } 98 | 99 | #: Houses 100 | houses = { 101 | 'Cusp 01': unicode(tr('Cusp 01')), 102 | 'Cusp 02': unicode(tr('Cusp 02')), 103 | 'Cusp 03': unicode(tr('Cusp 03')), 104 | 'Cusp 04': unicode(tr('Cusp 04')), 105 | 'Cusp 05': unicode(tr('Cusp 05')), 106 | 'Cusp 06': unicode(tr('Cusp 06')), 107 | 'Cusp 07': unicode(tr('Cusp 07')), 108 | 'Cusp 08': unicode(tr('Cusp 08')), 109 | 'Cusp 09': unicode(tr('Cusp 09')), 110 | 'Cusp 10': unicode(tr('Cusp 10')), 111 | 'Cusp 11': unicode(tr('Cusp 11')), 112 | 'Cusp 12': unicode(tr('Cusp 12')), 113 | 'Sector 01': unicode(tr('Sector 01')), 114 | 'Sector 02': unicode(tr('Sector 02')), 115 | 'Sector 03': unicode(tr('Sector 03')), 116 | 'Sector 04': unicode(tr('Sector 04')), 117 | 'Sector 05': unicode(tr('Sector 05')), 118 | 'Sector 06': unicode(tr('Sector 06')), 119 | 'Sector 07': unicode(tr('Sector 07')), 120 | 'Sector 08': unicode(tr('Sector 08')), 121 | 'Sector 09': unicode(tr('Sector 09')), 122 | 'Sector 10': unicode(tr('Sector 10')), 123 | 'Sector 11': unicode(tr('Sector 11')), 124 | 'Sector 12': unicode(tr('Sector 12')), 125 | 'Sector 13': unicode(tr('Sector 13')), 126 | 'Sector 14': unicode(tr('Sector 14')), 127 | 'Sector 15': unicode(tr('Sector 15')), 128 | 'Sector 16': unicode(tr('Sector 16')), 129 | 'Sector 17': unicode(tr('Sector 17')), 130 | 'Sector 18': unicode(tr('Sector 18')), 131 | 'Sector 19': unicode(tr('Sector 19')), 132 | 'Sector 20': unicode(tr('Sector 20')), 133 | 'Sector 21': unicode(tr('Sector 21')), 134 | 'Sector 22': unicode(tr('Sector 22')), 135 | 'Sector 23': unicode(tr('Sector 23')), 136 | 'Sector 24': unicode(tr('Sector 24')), 137 | 'Sector 25': unicode(tr('Sector 25')), 138 | 'Sector 26': unicode(tr('Sector 26')), 139 | 'Sector 27': unicode(tr('Sector 27')), 140 | 'Sector 28': unicode(tr('Sector 28')), 141 | 'Sector 29': unicode(tr('Sector 29')), 142 | 'Sector 30': unicode(tr('Sector 30')), 143 | 'Sector 31': unicode(tr('Sector 31')), 144 | 'Sector 32': unicode(tr('Sector 32')), 145 | 'Sector 33': unicode(tr('Sector 33')), 146 | 'Sector 34': unicode(tr('Sector 34')), 147 | 'Sector 35': unicode(tr('Sector 35')), 148 | 'Sector 36': unicode(tr('Sector 36')) 149 | } 150 | 151 | #: Objects (planets and houses) 152 | objects = dict() 153 | for k, v in planets.items(): 154 | objects[k] = v 155 | for k, v in houses.items(): 156 | objects[k] = v 157 | 158 | 159 | #: Aspects 160 | aspects = { 161 | 'Conjunction': unicode(tr('Conjunction')), 162 | 'Opposition': unicode(tr('Opposition')), 163 | 'Trine': unicode(tr('Trine')), 164 | 'Square': unicode(tr('Square')), 165 | 'Sextile': unicode(tr('Sextile')), 166 | 'Quincunx': unicode(tr('Quincunx')), 167 | 'SesquiSquare': unicode(tr('SesquiSquare')), 168 | 'SemiSquare': unicode(tr('SemiSquare')), 169 | 'SemiSextile': unicode(tr('SemiSextile')), 170 | 'SquiSquare': unicode(tr('SquiSquare')), 171 | 'SquiSextile': unicode(tr('SquiSextile')), 172 | 'Quintile': unicode(tr('Quintile')), 173 | 'BiQuintile': unicode(tr('BiQuintile')), 174 | 'SemiQuintile': unicode(tr('SemiQuintile')), 175 | 'Novile': unicode(tr('Novile')), 176 | 'BiNovile': unicode(tr('BiNovile')), 177 | 'QuadriNovile': unicode(tr('QuadriNovile')), 178 | 'SemiNovile': unicode(tr('SemiNovile')), 179 | 'Septile': unicode(tr('Septile')), 180 | 'BiSeptile': unicode(tr('BiSeptile')), 181 | 'TriSeptile': unicode(tr('TriSeptile')), 182 | 'Undecile': unicode(tr('Undecile')), 183 | 'BiUndecile': unicode(tr('BiUndecile')), 184 | 'TriUndecile': unicode(tr('TriUndecile')), 185 | 'QuadUndecile': unicode(tr('QuadUndecile')), 186 | 'QuinUndecile': unicode(tr('QuinUndecile')) 187 | } 188 | 189 | #: Available house systems 190 | houseSystems = [ ## order matters so it's a list 191 | ('P', unicode(tr('Placidus'))), 192 | ('K', unicode(tr('Koch'))), 193 | ('R', unicode(tr('Regiomontanus'))), 194 | ('C', unicode(tr('Campanus'))), 195 | ('B', unicode(tr('Alcabitus'))), 196 | ('O', unicode(tr('Porphyrus'))), 197 | ('A', unicode(tr('Equal'))), 198 | ('H', unicode(tr('Aziumtal/horizontal'))), 199 | ('V', unicode(tr('Vehlow equal'))), 200 | ('X', unicode(tr('Axial rotation'))), 201 | ('G', unicode(tr('Gauquelin sectors'))), 202 | ('U', unicode(tr('Krusinski'))), 203 | ('W', unicode(tr('Whole sign'))) 204 | ] 205 | 206 | #: Sidereal modes 207 | sidModes = [ 208 | (-1, unicode(tr('None (western tropical)'))), 209 | (0, unicode(tr('Fagan-Bradley'))), 210 | (1, unicode(tr('Lahiri'))), 211 | (2, unicode(tr('Deluce'))), 212 | (3, unicode(tr('Raman'))), 213 | (4, unicode(tr('Ushashashi'))), 214 | (5, unicode(tr('Krishnamurti'))), 215 | (6, unicode(tr('Djwhal Khul'))), 216 | (7, unicode(tr('Yukteshwar'))), 217 | (8, unicode(tr('Jn Bhasin'))), 218 | (9, unicode(tr('Babyl. Kugler 1'))), 219 | (10, unicode(tr('Babyl. Kugler 2'))), 220 | (11, unicode(tr('Babyl. Kugler 3'))), 221 | (12, unicode(tr('Babyl. Huber'))), 222 | (13, unicode(tr('Babyl. ETPSC'))), 223 | (14, unicode(tr('Aldebaran 15Tau'))), 224 | (15, unicode(tr('Hipparchos'))), 225 | (16, unicode(tr('Sassanian'))), 226 | (17, unicode(tr('Gal. Center 0Sag'))), 227 | (18, unicode(tr('J2000'))), 228 | (19, unicode(tr('J1900'))), 229 | (20, unicode(tr('B1950'))), 230 | (255, unicode(tr('User-defined'))) 231 | ] 232 | 233 | 234 | 235 | # End. 236 | -------------------------------------------------------------------------------- /oroboros/gui/dockwidget.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Dock widget area. 6 | 7 | """ 8 | 9 | from PyQt4.QtGui import * 10 | from PyQt4.QtCore import * 11 | 12 | from oroboros.gui import app 13 | from oroboros.gui import chthtml 14 | 15 | 16 | __all__ = ['DockWidget'] 17 | 18 | 19 | class DockWidget(QDockWidget): 20 | """Charts information area.""" 21 | 22 | def __init__(self, parent): 23 | QDockWidget.__init__(self, parent.tr('Charts Information'), parent) 24 | self._parent = parent 25 | self.setAllowedAreas(Qt.LeftDockWidgetArea|Qt.RightDockWidgetArea) 26 | self.setMinimumWidth(300) 27 | # tabs widget 28 | self.tabs = MultiChartTabWidget(self) 29 | self.connect(self.tabs, SIGNAL('currentChanged(int)'), 30 | parent.dockTabChangedEvent) 31 | self.setWidget(self.tabs) 32 | 33 | def addTab(self, idx): 34 | """Create tab for a multichart.""" 35 | self.tabs.addTab(idx) 36 | 37 | def removeTab(self, idx): 38 | """Destroy multichart.""" 39 | self.tabs.removeTab(idx) 40 | 41 | def resetTab(self, idx): 42 | """Reload multichart.""" 43 | self.tabs.setTabText(idx, app.desktop.charts[idx][0]._name) 44 | self.tabs.widget(idx).reset() 45 | 46 | 47 | 48 | class MultiChartTabWidget(QTabWidget): 49 | """Multi-charts container.""" 50 | 51 | def addTab(self, idx): 52 | tab = ChartTabWidget(idx, self) 53 | QTabWidget.addTab(self, tab, app.desktop.charts[idx][0]._name) 54 | 55 | def tabRemoved(self, idx): 56 | """Reset idx.""" 57 | for i in range(len(app.desktop.charts)): 58 | self.widget(i).resetIdx(i) 59 | 60 | 61 | 62 | class ChartTabWidget(QTabWidget): 63 | """Charts container.""" 64 | 65 | def __init__(self, idx, parent): 66 | QTabWidget.__init__(self, parent) 67 | self._idx = idx 68 | self.reset() 69 | 70 | def tabRemoved(self, idx): 71 | """Reset charts nums.""" 72 | for i in range(len(app.desktop.charts[self._idx])): 73 | self.widget(i).resetNum(i) 74 | 75 | def resetIdx(self, idx): 76 | self._idx = idx 77 | for i in range(len(app.desktop.charts[self._idx])): 78 | self.widget(i).resetIdx(idx) 79 | 80 | def reset(self): 81 | num = len(app.desktop.charts[self._idx]) 82 | for i in range(num): 83 | try: 84 | self.widget(i).reset() 85 | except: 86 | tab = ChartInfoToolBox(self._idx, i) 87 | self.addTab(tab, 88 | unicode(self.tr('Chart %(num)s')) % {'num': i+1}) 89 | # remove tabs or add/reset comparison info 90 | if num == 2: 91 | try: # update or create comparison tab 92 | self.widget(2).reset() 93 | except: 94 | self.addTab(CompareInfoToolBox(self._idx), self.tr('1 / 2')) 95 | else: # remove tabs 96 | for i in range(self.count() - num): 97 | self.removeTab(self.count() - 1) 98 | 99 | 100 | 101 | class ChartInfoToolBox(QToolBox): 102 | """Display chart data.""" 103 | 104 | def __init__(self, idx, num): 105 | QToolBox.__init__(self) 106 | self._idx = idx 107 | self._num = num 108 | tr = self.tr 109 | # chart data 110 | scroll1 = QScrollArea(self) 111 | scroll1.setWidgetResizable(True) 112 | self.addItem(scroll1, tr('Data')) 113 | self.textEdit1 = QTextEdit(self) 114 | self.textEdit1.setReadOnly(True) 115 | scroll1.setWidget(self.textEdit1) 116 | # chart planets 117 | scroll2 = QScrollArea(self) 118 | scroll2.setWidgetResizable(True) 119 | self.addItem(scroll2, tr('Planets')) 120 | self.textEdit2 = QTextEdit(self) 121 | self.textEdit2.setReadOnly(True) 122 | scroll2.setWidget(self.textEdit2) 123 | # chart houses 124 | scroll3 = QScrollArea(self) 125 | scroll3.setWidgetResizable(True) 126 | self.addItem(scroll3, tr('Cusps')) 127 | self.textEdit3 = QTextEdit(self) 128 | self.textEdit3.setReadOnly(True) 129 | scroll3.setWidget(self.textEdit3) 130 | # chart aspects 131 | scroll4 = QScrollArea(self) 132 | scroll4.setWidgetResizable(True) 133 | self.addItem(scroll4, tr('Aspects')) 134 | self.textEdit4 = QTextEdit(self) 135 | self.textEdit4.setReadOnly(True) 136 | scroll4.setWidget(self.textEdit4) 137 | # chart midpoints 138 | if app.desktop.charts[self._idx][num]._filter._calc_midp: 139 | self.createMidPointsTab() 140 | self.has_midpoints = True 141 | else: 142 | self.has_midpoints = False 143 | # load data 144 | self.reset() 145 | 146 | def createMidPointsTab(self): 147 | scroll = QScrollArea(self) 148 | scroll.setWidgetResizable(True) 149 | self.insertItem(4, scroll, self.tr('MidPoints')) 150 | self.textEdit5 = QTextEdit(self) 151 | self.textEdit5.setReadOnly(True) 152 | scroll.setWidget(self.textEdit5) 153 | self.has_midpoints = True 154 | 155 | def removeMidPointsTab(self): 156 | self.removeItem(4) 157 | self.has_midpoints = False 158 | 159 | def reset(self): 160 | self.createData() 161 | self.createPlanets() 162 | self.createHouses() 163 | self.createAspects() 164 | if app.desktop.charts[self._idx][self._num]._filter._calc_midp: 165 | if not self.has_midpoints: 166 | self.createMidPointsTab() 167 | self.createMidPoints() 168 | else: 169 | if self.has_midpoints: 170 | self.removeMidPointsTab() 171 | 172 | def createData(self): 173 | txt = chthtml.html_data( 174 | app.desktop.charts[self._idx][self._num]) 175 | self.textEdit1.setHtml(txt) 176 | 177 | def createPlanets(self): 178 | txt = chthtml.html_planets( 179 | app.desktop.charts[self._idx][self._num].planets) 180 | self.textEdit2.setHtml(txt) 181 | 182 | def createHouses(self): 183 | txt = chthtml.html_cusps( 184 | app.desktop.charts[self._idx][self._num].houses) 185 | self.textEdit3.setHtml(txt) 186 | 187 | def createAspects(self): 188 | txt = chthtml.html_aspects( 189 | app.desktop.charts[self._idx][self._num].aspects.sort_by_precision()) 190 | self.textEdit4.setHtml(txt) 191 | 192 | def createMidPoints(self): 193 | txt = chthtml.html_midpoints( 194 | app.desktop.charts[self._idx][self._num].midpoints, 195 | app.desktop.charts[self._idx][self._num].midp_aspects.sort_by_precision()) 196 | self.textEdit5.setHtml(txt) 197 | 198 | def resetIdx(self, idx): 199 | self._idx = idx 200 | 201 | def resetNum(self, num): 202 | self._num = num 203 | 204 | 205 | class CompareInfoToolBox(QToolBox): 206 | """Comparison info.""" 207 | 208 | def __init__(self, idx): 209 | QToolBox.__init__(self) 210 | self._idx = idx 211 | tr = self.tr 212 | # interaspects 213 | scroll1 = QScrollArea(self) 214 | scroll1.setWidgetResizable(True) 215 | self.addItem(scroll1, tr('Inter Aspects')) 216 | self.textEdit1 = QTextEdit(self) 217 | self.textEdit1.setReadOnly(True) 218 | scroll1.setWidget(self.textEdit1) 219 | # aspects to midpoints 1 220 | if app.desktop.charts[idx][0]._filter._calc_midp: 221 | self.createMidPoints1Tab() 222 | else: 223 | self.has_midp1 = False 224 | # aspects to midpoints 2 225 | if app.desktop.charts[idx][1]._filter._calc_midp: 226 | self.createMidPoints2Tab() 227 | else: 228 | self.has_midp2 = False 229 | # inter midpoints aspects 230 | if app.desktop.charts[idx][0]._filter._calc_midp and ( 231 | app.desktop.charts[idx][1]._filter._calc_midp): 232 | self.createInterMidPointsTab() 233 | else: 234 | self.has_intermidp = False 235 | self.reset() 236 | 237 | def createMidPoints1Tab(self): 238 | scroll2 = QScrollArea(self) 239 | scroll2.setWidgetResizable(True) 240 | self.addItem(scroll2, self.tr('Aspects MidPoints 1')) 241 | self.textEdit2 = QTextEdit(self) 242 | self.textEdit2.setReadOnly(True) 243 | scroll2.setWidget(self.textEdit2) 244 | self.has_midp1 = True 245 | 246 | def createMidPoints2Tab(self): 247 | scroll3 = QScrollArea(self) 248 | scroll3.setWidgetResizable(True) 249 | self.addItem(scroll3, self.tr('Aspects MidPoints 2')) 250 | self.textEdit3 = QTextEdit(self) 251 | self.textEdit3.setReadOnly(True) 252 | scroll3.setWidget(self.textEdit3) 253 | self.has_midp2 = 2 if self.has_midp1 else 1 254 | 255 | def createInterMidPointsTab(self): 256 | scroll4 = QScrollArea(self) 257 | scroll4.setWidgetResizable(True) 258 | self.addItem(scroll4, self.tr('Inter MidPoints')) 259 | self.textEdit4 = QTextEdit(self) 260 | self.textEdit4.setReadOnly(True) 261 | scroll4.setWidget(self.textEdit4) 262 | self.has_intermidp = True 263 | 264 | def reset(self): 265 | self.createAspects() 266 | if app.desktop.charts[self._idx][0]._filter._calc_midp: 267 | if not self.has_midp1: 268 | self.createMidPoints1Tab() 269 | self.createMidPoints1() 270 | else: 271 | if self.has_midp1: 272 | self.removeItem(1) 273 | self.has_midp1 = False 274 | if app.desktop.charts[self._idx][1]._filter._calc_midp: 275 | if not self.has_midp2: 276 | self.createMidPoints2Tab() 277 | self.createMidPoints2() 278 | else: 279 | if self.has_midp2: 280 | self.removeItem(self.has_midp2) 281 | self.has_midp2 = False 282 | if app.desktop.charts[self._idx][0]._filter._calc_midp and ( 283 | app.desktop.charts[self._idx][1]._filter._calc_midp): 284 | if not self.has_intermidp: 285 | self.createInterMidPointsTab() 286 | self.createInterMidPoints() 287 | else: 288 | if self.has_intermidp: 289 | self.removeItem(self.count()-1) 290 | self.has_intermidp = False 291 | 292 | def createAspects(self): 293 | txt = chthtml.html_aspects( 294 | app.desktop.charts[self._idx].interaspects.sort_by_precision()) 295 | self.textEdit1.setHtml(txt) 296 | 297 | def createMidPoints1(self): 298 | txt = chthtml.html_intermidp( 299 | app.desktop.charts[self._idx].intermidp1.sort_by_precision()) 300 | self.textEdit2.setHtml(txt) 301 | 302 | def createMidPoints2(self): 303 | txt = chthtml.html_intermidp( 304 | app.desktop.charts[self._idx].intermidp2.sort_by_precision()) 305 | self.textEdit3.setHtml(txt) 306 | 307 | def createInterMidPoints(self): 308 | txt = chthtml.html_intermidpoints( 309 | app.desktop.charts[self._idx].intermidpoints.sort_by_precision()) 310 | self.textEdit4.setHtml(txt) 311 | 312 | 313 | # End. 314 | -------------------------------------------------------------------------------- /oroboros/gui/chtpainter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Chart painter object. 6 | 7 | """ 8 | 9 | import os.path 10 | from math import sin, cos, radians 11 | 12 | from PyQt4.QtCore import * 13 | from PyQt4.QtGui import * 14 | 15 | import swisseph as swe 16 | 17 | from oroboros.gui import chtflowers 18 | 19 | 20 | __all__ = ['ChartPainter'] 21 | 22 | 23 | _baseDir = os.path.dirname(os.path.abspath(__file__)) 24 | 25 | _iconRect = QRect(0, 0, 300, 300) # original icons size 300x300 px 26 | 27 | # in case using others than default glyphs 28 | _zodiacDir = '.' 29 | _cuspsDir = '.' 30 | _planetsDir = '.' 31 | 32 | 33 | 34 | def _getPointAt(ang, hyp, modx=0, mody=0): 35 | """Get coordinates of a point. 36 | 37 | ang -> angle (0 is 3 o'clock, clockwise) 38 | hyp -> distance (hypothenuse) 39 | modx -> modification of point x leftwards 40 | mody -> modification of point y upwards 41 | 42 | """ 43 | x = (cos(radians(ang)) * hyp) - modx 44 | y = (sin(radians(ang)) * hyp) - mody 45 | return x, y 46 | 47 | 48 | class ChartPainter(QPainter): 49 | """Paint chart on device.""" 50 | 51 | def __init__(self, device, chart): 52 | """Paint on device. 53 | 54 | :type device: QPaintDevice 55 | :type chart: BiChart 56 | """ 57 | QPainter.__init__(self, device) 58 | self._chart = chart 59 | # set angle to vernal point 60 | self._vernalAng = swe.degnorm(chart[0]._houses[0]._longitude - 180) 61 | # set canvas 62 | self.setWindow(0, 0, 242, 242) # window is 240 x 240, +2 for antialias 63 | self.translate(121, 121) # set center at 0;0 64 | self.setRenderHints(QPainter.Antialiasing) 65 | # set colors 66 | if chart[0].filter._bg_color == 'black': 67 | self._bgColor = Qt.black 68 | self._drawColor = Qt.white 69 | self._lightColor = Qt.darkGray 70 | self._middleColor = Qt.gray 71 | self._darkColor = Qt.lightGray 72 | else: ## white 73 | self._bgColor = Qt.white 74 | self._drawColor = Qt.black 75 | self._lightColor = Qt.lightGray 76 | self._middleColor = Qt.gray 77 | self._darkColor = Qt.darkGray 78 | # fill background color 79 | self.fillRect(-121, -121, 242, 242, QBrush(self._bgColor)) 80 | # set drawing color 81 | self.setPen(self._drawColor) 82 | # draw markers before circles to overwrite lighter pixels 83 | self.draw_zodiac() 84 | self.draw_cusps() 85 | self.draw_circles() 86 | self.draw_signs() 87 | self.draw_houses() 88 | # draw visible charts 89 | visible = [x for x in self._chart if not x._hidden] 90 | lenvisible = len(visible) 91 | if lenvisible == 0: ## all hidden 92 | return 93 | # draw aspects before planets to overwrite 94 | if lenvisible == 1: 95 | self.draw_aspects(visible[0]._all_draw_aspects(), lenvisible) 96 | self.draw_planets(0, lenvisible, visible[0]._all_draw_planets()) 97 | else: ## interaspects 98 | self.draw_aspects(self._chart._all_draw_aspects(), lenvisible) 99 | if self._chart[0]._filter._draw_midp: 100 | self.draw_planets(0, lenvisible, self._chart._all_draw_planets(0)) 101 | else: 102 | self.draw_planets(0, lenvisible, visible[0]._all_draw_planets()) 103 | if self._chart[1]._filter._draw_midp: 104 | self.draw_planets(1, lenvisible, self._chart._all_draw_planets(1)) 105 | else: 106 | self.draw_planets(1, lenvisible, visible[1]._all_draw_planets()) 107 | 108 | def draw_circles(self): 109 | """Draw all circles for zodiac, degree markers, houses.""" 110 | self.drawArc(-120, -120, 240, 240, 0, 5760) # most outer circle 111 | self.drawArc(-100, -100, 200, 200, 0, 5760) # for zodiac 112 | self.drawArc(-93, -93, 186, 186, 0, 5760) # for degree markers 113 | self.drawArc(-80, -80, 160, 160, 0, 5760) # for houses markers 114 | 115 | def draw_zodiac(self): 116 | """Draw zodiac limits and degrees markers.""" 117 | self.save() 118 | zodiacline = (93, 0, 120, 0) 119 | degreeline = (93, 0, 100, 0) 120 | self.rotate(self._vernalAng) 121 | for i in range(360): 122 | if i % 30 == 0: # sign limit 123 | self.setPen(self._drawColor) 124 | self.drawLine(*zodiacline) 125 | elif i % 10 == 0: # decanate limit 126 | self.setPen(self._darkColor) 127 | self.drawLine(*degreeline) 128 | elif i % 5 == 0: # half-decanate limit 129 | self.setPen(self._middleColor) 130 | self.drawLine(*degreeline) 131 | else: # one degree 132 | self.setPen(self._lightColor) 133 | self.drawLine(*degreeline) 134 | self.rotate(1) 135 | self.restore() 136 | 137 | def draw_signs(self): 138 | """Draw signs glyphs.""" 139 | zodiacdir = os.path.join(_baseDir, 'icons', 'zodiac', _zodiacDir) 140 | ang = swe.degnorm(self._vernalAng - 15) 141 | for i in range(12): 142 | im = QImage(os.path.join(zodiacdir, 'sign_%.2d.png' % (i+1))) 143 | x, y = _getPointAt(ang, 111, 8, 8) 144 | target = QRect(x, y, 16, 16) 145 | self.drawImage(target, im, _iconRect) 146 | ang -= 30 147 | 148 | def draw_cusps(self): 149 | """Draw houses cusps.""" 150 | # get params, Gauquelin has 36 cusps 151 | mc = self._vernalAng - self._chart[0]._houses[9]._longitude 152 | if self._chart[0].filter._hsys != 'G': 153 | othercusps = (1, 2, 4, 5) 154 | else: # Gauquelin sectors 155 | othercusps = (1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17) 156 | # draw mc line 157 | w, x = _getPointAt(mc, 122) 158 | y, z = _getPointAt(mc + 180, 122) 159 | self.setPen(self._darkColor) 160 | self.drawLine(w, x, y, z) 161 | # draw other cusps 162 | pen1 = QPen(Qt.DotLine) 163 | pen1.setColor(self._lightColor) 164 | pen2 = QPen(self._middleColor) 165 | for i in othercusps: 166 | self.setPen(pen1) 167 | a = self._vernalAng - self._chart[0]._houses[i].longitude 168 | w, x = _getPointAt(a, 80.5) 169 | y, z = _getPointAt(a + 180, 80.5) 170 | self.drawLine(w, x, y, z) 171 | self.setPen(pen2) 172 | w1, x1 = _getPointAt(a, 93.5) 173 | y1, z1 = _getPointAt(a + 180, 93.5) 174 | self.drawLine(w, x, w1, x1) 175 | self.drawLine(y, z, y1, z1) 176 | # draw asc line finally to overwrite lighter pixels 177 | self.setPen(self._darkColor) 178 | self.drawLine(-121, 0, 121, 0) 179 | # reset pen 180 | self.setPen(self._drawColor) 181 | 182 | def draw_houses(self): 183 | """Draw houses glyphs.""" 184 | cuspsdir = os.path.join(_baseDir, 'icons', 'cusps', _cuspsDir) 185 | if self._chart[0].filter._hsys != 'G': ## traditional houses 186 | houses = self._chart[0]._houses[:12] 187 | for i in range(12): 188 | im = QImage(os.path.join(cuspsdir, 'cusp_%.2d.png' % (i+1))) 189 | diff = swe.difdeg2n(houses[i]._longitude, 190 | houses[i-11]._longitude) / 2.0 191 | a = self._vernalAng - (houses[i]._longitude - diff) 192 | x, y = _getPointAt(a, 87, 5, 5) 193 | target = QRect(x, y, 10, 10) 194 | self.drawImage(target, im, _iconRect) 195 | else: ## Gauquelin sectors 196 | houses = self._chart[0]._houses[:36] 197 | for i in range(36): 198 | im = QImage(os.path.join(cuspsdir, 'sector_%.2d.png' % (i+1))) 199 | diff = swe.difdeg2n(houses[i]._longitude, 200 | houses[i-35]._longitude) / 2.0 201 | a = self._vernalAng - (houses[i]._longitude - diff) 202 | x, y = _getPointAt(a, 87, 5, 5) 203 | target = QRect(x, y, 10, 10) 204 | self.drawImage(target, im, _iconRect) 205 | 206 | def draw_planets(self, num, lenvisible, plres): 207 | """Draw planets glyphs.""" 208 | if lenvisible == 2: 209 | if num == 1: 210 | w1, w2, w3, w4, gw = 50, 53, 68, 73, 16 211 | self.setPen(self._drawColor) 212 | elif num == 0: 213 | w1, w2, w3, w4, gw = 50, 53, 56, 61, 15 214 | self.setPen(self._lightColor) 215 | else: 216 | w1, w2, w3, w4, gw = 55, 58, 63, 70, 18 217 | self.setPen(self._drawColor) 218 | pldir = os.path.join(_baseDir, 'icons', 'planets', _planetsDir) 219 | plres.sort_by_ranking(reverse=True) 220 | # get arranged positions 221 | allpos = [x._longitude for x in plres] 222 | glyph = chtflowers.rearrange(allpos, 10) 223 | # draw midpoints 224 | for i, res in enumerate(plres): 225 | a = self._vernalAng - glyph[i] 226 | b = self._vernalAng - res._longitude 227 | # draw little marker 228 | if a == b: 229 | w, x = _getPointAt(b, w3) 230 | y, z = _getPointAt(b, w1) 231 | self.drawLine(w, x, y, z) 232 | else: 233 | w, x = _getPointAt(b, w2) 234 | y, z = _getPointAt(b, w1) 235 | self.drawLine(w, x, y, z) 236 | y, z = _getPointAt(a, w3) 237 | self.drawLine(w, x, y, z) 238 | # paste midp or planet glyph 239 | try: ## planet 240 | im = QImage(os.path.join(pldir, res._planet._glyph)) 241 | x, y = _getPointAt(a, w4, gw/2.0, gw/2.0) 242 | target = QRect(x, y, gw, gw) 243 | self.drawImage(target, im, _iconRect) 244 | except: ## midp 245 | im1 = QImage(os.path.join(pldir, res._data1._planet._glyph)) 246 | im2 = QImage(os.path.join(pldir, res._data2._planet._glyph)) 247 | x, y = _getPointAt(a, w4+3, gw/3.2, gw/3.2) 248 | target = QRect(x, y, gw/1.6, gw/1.6) 249 | self.drawImage(target, im1, _iconRect) 250 | x, y = _getPointAt(a, w4-3, gw/3.2, gw/3.2) 251 | target = QRect(x, y, gw/1.6, gw/1.6) 252 | self.drawImage(target, im2, _iconRect) 253 | self.setPen(self._drawColor) 254 | 255 | def draw_aspects(self, aspectsres, numvisible): 256 | if numvisible == 1: 257 | width = 55 258 | else: ## 2 259 | width = 50 260 | for aspr in aspectsres.sort_by_ranking(): 261 | a1 = self._vernalAng - aspr._data1._longitude 262 | a2 = self._vernalAng - aspr._data2._longitude 263 | w, x = _getPointAt(a1, width) 264 | y, z = _getPointAt(a2, width) 265 | pen = QPen(QColor(*tuple(aspr._aspect._color))) 266 | # dash line according to factor 267 | if aspr._factor > 0.1: 268 | if aspr._factor <= 0.2: 269 | pen.setDashPattern([8, 2]) 270 | elif aspr._factor <= 0.3: 271 | pen.setDashPattern([6, 2]) 272 | elif aspr._factor <= 0.4: 273 | pen.setDashPattern([5, 2]) 274 | elif aspr._factor <= 0.5: 275 | pen.setDashPattern([4, 2]) 276 | elif aspr._factor <= 0.6: 277 | pen.setDashPattern([3, 3]) 278 | elif aspr._factor <= 0.7: 279 | pen.setDashPattern([2, 4]) 280 | elif aspr._factor <= 0.8: 281 | pen.setDashPattern([1, 5]) 282 | elif aspr._factor <= 0.9: 283 | pen.setDashPattern([1, 6]) 284 | elif aspr._factor <= 1: 285 | pen.setDashPattern([1, 7]) 286 | self.setPen(pen) 287 | self.drawLine(w, x, y, z) 288 | self.setPen(self._drawColor) 289 | 290 | 291 | 292 | 293 | # End. 294 | -------------------------------------------------------------------------------- /oroboros/core/aspectsresults.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Object holding aspects calculations results. 6 | 7 | Provides functions for searching, retrieving, sorting results. 8 | 9 | """ 10 | 11 | from oroboros.core.aspects import Aspect 12 | from oroboros.core.results import PlanetData, MidPointData 13 | 14 | 15 | __all__ = ['AspectData', 'AspectDataList', 'MidPointAspectData', 16 | 'MidPointAspectDataList', 'InterMidPointAspectData', 17 | 'InterMidPointAspectDataList'] 18 | 19 | 20 | 21 | class AspectData(object): 22 | """Aspect data element.""" 23 | 24 | __slots__ = ('_data1', '_data2', '_aspect', '_diff', '_apply', '_factor') 25 | 26 | def _get_data1(self): 27 | """Get planet 1 data. 28 | 29 | :rtype: PlanetData 30 | """ 31 | return self._data1 32 | 33 | def _set_data1(self, data): 34 | """Set planet 1 data. 35 | 36 | :type data: PlanetData 37 | :raise TypeError: invalid data 38 | """ 39 | if not isinstance(data, PlanetData): 40 | raise TypeError('Invalid planet data %s.' % data) 41 | self._data1 = data 42 | 43 | def _get_data2(self): 44 | """Get planet 2 data. 45 | 46 | :rtype: PlanetData 47 | """ 48 | return self._data2 49 | 50 | def _set_data2(self, data): 51 | """Set planet 2 data. 52 | 53 | :type data: PlanetData 54 | :raise TypeError: invalid data 55 | """ 56 | if not isinstance(data, PlanetData): 57 | raise TypeError('Invalid planet data %s.' % data) 58 | self._data2 = data 59 | 60 | def _get_aspect(self): 61 | """Get aspect object. 62 | 63 | :rtype: Aspect 64 | """ 65 | return self._aspect 66 | 67 | def _set_aspect(self, asp): 68 | """Set aspect object. 69 | 70 | Accepts aspect object or aspect name or index. 71 | 72 | :type asp: Aspect or str or int 73 | :raise TypeError: invalid aspect 74 | """ 75 | if not isinstance(asp, Aspect): 76 | try: 77 | asp = Aspect(asp) 78 | except: 79 | raise TypeError('Invalid aspect object %s.' % asp) 80 | self._aspect = asp 81 | 82 | def _get_diff(self): 83 | """Get difference with exact (no orb) aspect. 84 | 85 | :rtype: float 86 | """ 87 | return self._diff 88 | 89 | def _set_diff(self, diff): 90 | """Set difference. 91 | 92 | :type diff: float 93 | """ 94 | self._diff = diff 95 | 96 | def _get_apply(self): 97 | """Get applying flag. 98 | 99 | - True -> applying 100 | - False -> separating 101 | - None -> stable 102 | 103 | :rtype: bool or None 104 | """ 105 | return self._apply 106 | 107 | def _set_apply(self, apply): 108 | """Set applying flag. 109 | 110 | :type apply: bool or None 111 | :raise TypeError: invalid flag 112 | """ 113 | if apply not in (True, False, None): 114 | raise TypeError('Invalid apply flag %s.' % apply) 115 | self._apply = apply 116 | 117 | def _get_factor(self): 118 | """Get factor (percentage of orb used). 119 | 120 | :rtype: float 121 | """ 122 | return self._factor 123 | 124 | def _set_factor(self, factor): 125 | """Set factor. 126 | 127 | :type factor: float 128 | """ 129 | self._factor = factor 130 | 131 | data1 = property(_get_data1, _set_data1, doc='First planet data.') 132 | data2 = property(_get_data2, _set_data2, doc='Second planet data') 133 | aspect = property(_get_aspect, _set_aspect, doc='Aspect object.') 134 | diff = property(_get_diff, _set_diff, doc='Difference (numeric).') 135 | apply = property(_get_apply, _set_apply, doc='Apply flag.') 136 | factor = property(_get_factor, _set_factor, doc='Strength factor.') 137 | 138 | def __init__(self, data1, data2, asp, diff, apply, factor): 139 | self.data1 = data1 140 | self.data2 = data2 141 | self.aspect = asp 142 | self.diff = diff 143 | self.apply = apply 144 | self.factor = factor 145 | 146 | def __str__(self): 147 | return str([self._data1, self._data2, self._aspect, self._diff, 148 | self._apply, self._factor]) 149 | 150 | def __repr__(self): 151 | return 'AspectData(%s, %s, %s, %s, %s, %s)' % ( 152 | repr(x) for x in ( 153 | self._data1, self._data2, self._aspect, self._diff, self._apply, 154 | self._factor)) 155 | 156 | 157 | class AspectDataList(list): 158 | """Aspects data list.""" 159 | 160 | def feed(self, data1, data2, asp, diff, apply, factor): 161 | """Append calculation results. 162 | 163 | - data* -> PlanetData 164 | - asp -> Aspect 165 | - diff, apply, factor -> swisseph.match_aspect results 166 | 167 | """ 168 | self.append(AspectData(data1, data2, asp, diff, apply, factor)) 169 | 170 | def sort_by_precision(self, reverse=False): 171 | """Sort results by precision. 172 | 173 | :rtype: self 174 | """ 175 | self.sort(self._sort_by_precision, reverse=reverse) 176 | return self 177 | 178 | @staticmethod 179 | def _sort_by_precision(x, y): 180 | """Function to sort aspects list by aspect precision.""" 181 | if x._diff > y._diff: 182 | return 1 183 | elif x._diff < y._diff: 184 | return -1 185 | return 0 186 | 187 | def sort_by_ranking(self, reverse=False): 188 | """Sort results by aspect ranking. 189 | 190 | :rtype: self 191 | """ 192 | self.sort(self._sort_by_ranking, reverse=reverse) 193 | return self 194 | 195 | @staticmethod 196 | def _sort_by_ranking(x, y): 197 | """Function to sort results list by aspect ranking.""" 198 | if x._aspect._ranking > y._aspect._ranking: 199 | return 1 200 | elif x._aspect._ranking < y._aspect._ranking: 201 | return -1 202 | return 0 203 | 204 | def sort_by_factor(self, reverse=False): 205 | """Sort aspects list by aspect strength factor. 206 | 207 | :rtype: self 208 | """ 209 | self.sort(self._sort_by_factor, reverse=reverse) 210 | return self 211 | 212 | @staticmethod 213 | def _sort_by_factor(x, y): 214 | """Function to sort results by strength factor.""" 215 | if x._factor > y._factor: 216 | return 1 217 | elif x._factor < y._factor: 218 | return -1 219 | return 0 220 | 221 | def __getitem__(self, aspname): 222 | """Get all results for an aspect. 223 | 224 | :type aspname: str 225 | :rtype: AspectDataList 226 | """ 227 | ret = AspectDataList() 228 | for e in self: 229 | if e._aspect._name == aspname: 230 | ret.append(e) 231 | return ret 232 | 233 | 234 | class MidPointAspectData(AspectData): 235 | """Aspect data for midpoints/planets.""" 236 | 237 | __slots__ = ('_data1', '_data2', '_aspect', '_diff', 238 | '_apply', '_factor') 239 | 240 | def _get_data1(self): 241 | """Get midpoint data. 242 | 243 | :rtype: MidPointData 244 | """ 245 | return self._data1 246 | 247 | def _set_data1(self, mpdata): 248 | """Set midpoint data. 249 | 250 | :type mpdata: MidPointData 251 | :raise TypeError: invalid data 252 | """ 253 | if not isinstance(mpdata, MidPointData): 254 | raise TypeError('Invalid midpoint data %s.' % mpdata) 255 | self._data1 = mpdata 256 | 257 | data1 = property(_get_data1, _set_data1, 258 | doc='Midpoint data.') 259 | data2 = property(AspectData._get_data2, AspectData._set_data2, 260 | doc='Planet data.') 261 | aspect = property(AspectData._get_aspect, AspectData._set_aspect, 262 | doc='Aspect object.') 263 | diff = property(AspectData._get_diff, AspectData._set_diff, 264 | doc='Difference.') 265 | apply = property(AspectData._get_apply, AspectData._set_apply, 266 | doc='Apply flag.') 267 | factor = property(AspectData._get_factor, AspectData._set_factor, 268 | doc='Strength factor.') 269 | 270 | def __repr__(self): 271 | return 'MidPointAspectData(%s, %s, %s, %s, %s, %s)' % ( 272 | repr(x) for x in ( 273 | self._data1, self._data2, self._aspect, 274 | self._diff, self._apply, self._factor)) 275 | 276 | 277 | class MidPointAspectDataList(AspectDataList): 278 | """Aspects data list for midpoints/planets.""" 279 | 280 | def feed(self, data1, data2, asp, diff, apply, factor): 281 | """Append an aspect result. 282 | 283 | :type data1: MidPointData 284 | :type data2: PlanetData 285 | :type asp: Aspect 286 | :type diff: numeric 287 | :type apply: bool or None 288 | :type factor: numeric 289 | """ 290 | self.append(MidPointAspectData(data1, data2, asp, diff, apply, factor)) 291 | 292 | def __getitem__(self, aspname): 293 | """Get all results for an aspect. 294 | 295 | :type aspname: str 296 | :rtype: MidPointAspectDataList 297 | """ 298 | ret = MidPointAspectDataList() 299 | for e in self: 300 | if e._aspect._name == aspname: 301 | ret.append(e) 302 | return ret 303 | 304 | def get_midpoints(self): 305 | """Get a set of midpoints data. 306 | 307 | :rtype: list 308 | """ 309 | ret = list() 310 | for res in self: 311 | if res._data1 not in ret: 312 | ret.append(res._data1) 313 | return ret 314 | 315 | 316 | class InterMidPointAspectData(MidPointAspectData): 317 | """Aspect data list for midpoints/midpoints.""" 318 | 319 | __slots__ = MidPointAspectData.__slots__ 320 | 321 | def _get_data2(self): 322 | """Get midpoint data. 323 | 324 | :rtype: MidPointData 325 | """ 326 | return self._data2 327 | 328 | def _set_data2(self, mpdata): 329 | """Set midpoint data. 330 | 331 | :type mpdata: MidPointData 332 | :raise TypeError: invalid data 333 | """ 334 | if not isinstance(mpdata, MidPointData): 335 | raise TypeError('Invalid midpoint data %s.' % mpdata) 336 | self._data2 = mpdata 337 | 338 | data1 = property(MidPointAspectData._get_data1, MidPointAspectData._set_data1, 339 | doc='Midpoint data.') 340 | data2 = property(_get_data2, _set_data2, 341 | doc='Planet data.') 342 | aspect = property(MidPointAspectData._get_aspect, MidPointAspectData._set_aspect, 343 | doc='Aspect object.') 344 | diff = property(MidPointAspectData._get_diff, MidPointAspectData._set_diff, 345 | doc='Difference.') 346 | apply = property(MidPointAspectData._get_apply, MidPointAspectData._set_apply, 347 | doc='Apply flag.') 348 | factor = property(MidPointAspectData._get_factor, MidPointAspectData._set_factor, 349 | doc='Strength factor.') 350 | 351 | def __repr__(self): 352 | return 'InterMidPointAspectData(%s, %s, %s, %s, %s, %s)' % ( 353 | repr(x) for x in ( 354 | self._data1, self._data2, self._aspect, 355 | self._diff, self._apply, self._factor)) 356 | 357 | 358 | class InterMidPointAspectDataList(MidPointAspectDataList): 359 | """List of midpoints/midpoints aspects.""" 360 | 361 | def feed(self, data1, data2, asp, diff, apply, factor): 362 | """Append an aspect result. 363 | 364 | :type data1: MidPointData 365 | :type data2: MidPointData 366 | :type asp: Aspect 367 | :type diff: numeric 368 | :type apply: bool or None 369 | :type factor: numeric 370 | """ 371 | self.append(InterMidPointAspectData(data1, data2, asp, diff, apply, factor)) 372 | 373 | def __getitem__(self, aspname): 374 | """Get all results for an aspect. 375 | 376 | :type aspname: str 377 | :rtype: InterMidPointAspectDataList 378 | """ 379 | ret = InterMidPointAspectDataList() 380 | for e in self: 381 | if e._aspect._name == aspname: 382 | ret.append(e) 383 | return ret 384 | 385 | def get_midpoints2(self): 386 | """Get a set of midpoints data. 387 | 388 | :rtype: list 389 | """ 390 | ret = list() 391 | for res in self: 392 | if res._data2 not in ret: 393 | ret.append(res._data2) 394 | return ret 395 | 396 | 397 | 398 | 399 | def _test(): 400 | import doctest 401 | doctest.testmod() 402 | 403 | if __name__ == '__main__': 404 | _test() 405 | 406 | # End. 407 | --------------------------------------------------------------------------------