diff --git a/.git_archival.txt b/.git_archival.txt
new file mode 100644
index 00000000..65215e63
--- /dev/null
+++ b/.git_archival.txt
@@ -0,0 +1,3 @@
+node: $Format:%H$
+node-date: $Format:%cI$
+describe-name: $Format:%(describe:tags=true,match=v*[0-9]*)$
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..00a7b00c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+.git_archival.txt export-subst
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index fdace1f7..bfb09df2 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -60,7 +60,7 @@ jobs:
PYTRAINER_ALCHEMYURL: ${{ matrix.database_url }}
TZ: Europe/Kaliningrad
LC_TIME: C
- run: xvfb-run python3 -Wall setup.py test
+ run: xvfb-run python3 -Wall -m unittest
test_tox:
runs-on: ubuntu-latest
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index 37ff3bc4..00000000
--- a/INSTALL
+++ /dev/null
@@ -1,43 +0,0 @@
-pytrainer basic installation
-===========================
-
-These are generic installation instructions to use with *.tar.gz files
-
-1.- Dependency Overview
-
-Here are the dependencies for pytrainer. Of course you must have a working environment
-with proper shell configuration and typical GNU tools to uncompress (gunzip) and untar
-(tar) files.
-
-1.1.- Packages
-
-Required Python packages and their versions can be seen in pyproject.toml. Additionally
-the Python bindings for gobject-introspection and Cairo are required, as are the gir
-bindings for GTK. For map functionality the gir bindings for WebKit2 are also required.
-
-- Only needed if correspondent plugin or extension is enabled:
-gpsbabel == 1.3.5 ("GoogleEarth" and "Garmin via GPSBabel 1.3.5" aka "garmin_hr")
-garmintools >= 0.10 ("Import from Garmin GPS device (via garmintools)" aka "garmintools_full" plugin)
-wordpresslib (already distributed within pytrainer tarball, wordpress extension)
-httplib2 >= 0.6.0 (wordpress extension)
-SOAPpy >= 0.11.6 (dotclear extension)
-GDAL (Elevation correction, via "gdal-python" or "python-gdal")
-perl (garmin-fit plugin, tested with perl v5.16.2 on Fedora 18, see ticket #5)
-
-2.- Installation process
-
-Copy tarball file to a location where you have write and execution rights (e.g. /tmp or
-your $HOME directory). Make sure executables are under your $PATH.
-
-$ tar -xzf pytrainer-X.Y.Z.tar.gz
-$ cd pytrainer-X.Y.Z
-$ sudo python setup.py install
-$ pytrainer -i
-
-For more information about the process, please check http://docs.python.org/distutils/setupscript.html
-
-3.- USB access
-
-pytrainer can use gpsbabel (http://www.gpsbabel.org) to retrieve information from Garmin
-devices. There are some problems regarding driver to access usb ports, please take a look
-at http://www.gpsbabel.org/os/Linux_Hotplug.html
diff --git a/README.md b/README.md
index 8f4ee3f8..316f5740 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,54 @@
-pytrainer - Free your sports
-==================================================
+# pytrainer - Free your sports
pytrainer is a desktop application for logging and graphing sporting
-activities such as running or cycling sessions. Data can be
-imported from GPS devices, files or input manually. Currently
-pytrainer supports GPX, TCX, and FIT files.
-
-Source Repository Structure
----------------------------
-* **extensions** addons to extend pytrainer basic functionality
-* **glade** user interface design
-* **imports** files to parse different source formats
-* **locale** localization files
-* **man** source manpage
-* **plugins** files to retrieve data from different sources
-* **pytrainer** core files
-* **schemas** schemas to support correct xml parsing
-* **utils** localization shell script
-
-Installation from source tarball
------------------
-Copy tarball file to a location where you have write and execution rights (e.g. `/tmp` or your `$HOME` directory). Make sure executables are under your `$PATH`.
-
-`$ tar -xzf pytrainer-X.Y.Z.tar.gz`
-
-`$ cd pytrainer-X.Y.Z`
-
-`$ sudo python setup.py install`
-
-`$ pytrainer -i`
-
-For more information about the process, please check [Distutils documentation] (http://docs.python.org/distutils/setupscript.html)
-
-Further Resources
------------------
+activities such as running or cycling sessions. Data can be imported from GPS
+devices, files or input manually. Currently pytrainer supports GPX, TCX, and
+FIT files.
+
+## Installation
+Most popular Linux distributions (Debian, Ubuntu, Fedora and so on) already
+contain a pytrainer package, use that if available. If a package is not
+available for your system you can install from source, see below.
+
+### Installation from source tarball
+Copy tarball file to a location where you have write and execution rights (e.g. your `$HOME` directory).
+
+```shell
+tar -xzf pytrainer-X.Y.Z.tar.gz
+cd pytrainer-X.Y.Z
+pip install ."[gui]"
+pytrainer
+```
+
+### Installation into a venv (for development)
+This installation method is very similar to the basic source install above.
+For development it makes more sense to start from a git clone instead of a
+tarball, and also makes more sense to install into a virtual Python
+environment.
+
+```shell
+git clone https://github.com/pytrainer/pytrainer.git
+cd pytrainer
+python3 -m venv .venv
+.venv/bin/pip install -e ".[gui]"
+.venv/bin/pytrainer
+```
+
+### Additional packages
+For map functionality the GIR bindings for WebKit2 need to be installed.
+
+Certain plugins and extensions also need additional software:
+
+| Package | Plugin or extension |
+|---------------|-----------------------------------------------------------------------------------|
+| GPSBabel | Garmin via GPSBabel and Google Earth plugins (aka garmin-hr) |
+| garmintools | Import from Garmin GPS device (Garmin via garmintools plugin aka garmintools_full)|
+| Perl | Garmin FIT plugin |
+| wordpresslib | Wordpress extension, already distributed within pytrainer tarball |
+| httplib2 | Wordpress extension |
+| SOAPpy | Dotclear extension |
+| GDAL | Elevation correction extension, via gdal-python or python-gdal |
+
+## Further Resources
* FAQ [https://github.com/pytrainer/pytrainer/wiki/FAQ](https://github.com/pytrainer/pytrainer/wiki/FAQ)
* Distribution list: pytrainer-devel@lists.sourceforge.net
* Development guide: [https://github.com/pytrainer/pytrainer/wiki/Development-guide](https://github.com/pytrainer/pytrainer/wiki/Development-guide)
diff --git a/glade/profile.ui b/glade/profile.ui
index 4947c77d..62110258 100644
--- a/glade/profile.ui
+++ b/glade/profile.ui
@@ -1848,51 +1848,6 @@ Continue?
2
-
-
- True
- 0
- 5
- 5
- New Graph
-
-
- 5
- 6
-
-
-
-
- --newgraph
- True
- True
- False
- True
-
-
-
- 1
- 2
- 5
- 6
- 10
-
-
-
-
- True
- 0
- 5
- <small>Want to use experimental new approach to graphing?</small>
- True
-
-
- 2
- 3
- 5
- 6
-
-
diff --git a/glade/pytrainer.ui b/glade/pytrainer.ui
index 2bea5b02..fd930f9c 100644
--- a/glade/pytrainer.ui
+++ b/glade/pytrainer.ui
@@ -1534,100 +1534,6 @@
True
-
-
- True
- 5
- 8
-
-
- True
- True
- True
- Show graph display options
-
-
-
- True
- gtk-preferences
-
-
-
-
- False
- False
- 1
-
-
-
-
- True
-
- model2
-
-
-
-
-
- False
- 2
-
-
-
-
- True
- Versus
-
-
- False
- False
- 3
-
-
-
-
- True
-
- model3
-
-
-
-
-
- False
- False
- 4
-
-
-
-
-
-
-
- True
- True
- True
-
-
-
- True
- gtk-fullscreen
-
-
-
-
- False
- False
- end
- 0
-
-
-
-
- False
- 0
-
-
True
@@ -1720,699 +1626,10 @@
-
+
True
- True
- True
-
- True
-
-
- True
- <small>Graph Display Options</small>
- True
-
-
- False
- 0
-
-
-
-
- True
- 0
-
-
- True
- 10
-
-
- True
- 5
- 3
-
-
- True
- 1
- <small>Limits</small>
- True
-
-
- 1
- 2
-
-
-
-
-
- True
- <small>Min</small>
- True
-
-
- 1
- 2
-
-
-
-
-
- True
- <small>Max</small>
- True
-
-
- 2
- 3
-
-
-
-
-
- True
- True
- ●
- 4
- adjustment1
-
-
-
- 1
- 2
- 1
- 2
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- True
- ●
- 4
- adjustment2
-
-
-
- 2
- 3
- 1
- 2
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- 1
- <small>Color</small>
- True
-
-
- 2
- 3
-
-
-
-
-
- True
- True
- True
- 0
- #000000000000
-
-
-
- 1
- 2
- 2
- 3
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- 1
- <small>Weight</small>
- True
-
-
- 3
- 4
-
-
-
-
-
- True
- True
- 2
- ●
- adjustment3
-
-
-
- 1
- 2
- 3
- 4
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- Y1
-
-
-
-
-
-
-
- True
- False
- 1
- <small>Smoothing</small>
- True
-
-
- 4
- 5
-
-
-
-
-
- True
- False
- True
- 2
- ●
- adjustment4
-
-
- 1
- 2
- 4
- 5
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
- 2
-
-
-
-
- True
- False
- 0
-
-
- True
- 10
-
-
- True
- 5
- 3
-
-
- True
- 1
- <small>Limits</small>
- True
-
-
- 1
- 2
-
-
-
-
-
- True
- <small>Min</small>
- True
-
-
- 1
- 2
-
-
-
-
-
- True
- <small>Max</small>
- True
-
-
- 2
- 3
-
-
-
-
-
- True
- True
- ●
- 4
- adjustment5
-
-
- 1
- 2
- 1
- 2
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- True
- ●
- 4
- adjustment6
-
-
- 2
- 3
- 1
- 2
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- Y2
-
-
-
-
-
-
-
- True
- 1
- <small>Color</small>
- True
-
-
- 2
- 3
-
-
-
-
-
- True
- 1
- <small>Weight</small>
- True
-
-
- 3
- 4
-
-
-
-
-
- True
- True
- True
- 0
- #000000000000
-
-
- 1
- 2
- 2
- 3
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- True
- 2
- ●
- adjustment7
-
-
- 1
- 2
- 3
- 4
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- False
- 1
- <small>Smoothing</small>
- True
-
-
- 4
- 5
-
-
-
-
-
- True
- False
- True
- 2
- ●
- adjustment8
-
-
- 1
- 2
- 4
- 5
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
- 3
-
-
-
-
- True
- False
- 0
-
-
- True
- 12
-
-
- True
- 4
- 3
-
-
- True
- X
-
-
-
-
-
-
-
- True
- 1
- <small>Limits</small>
- True
-
-
- 1
- 2
-
-
-
-
-
- True
- True
- ●
- 4
- adjustment9
-
-
- 1
- 2
- 1
- 2
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- True
- ●
- 4
- adjustment10
-
-
- 2
- 3
- 1
- 2
- GTK_SHRINK
- GTK_SHRINK
-
-
-
-
- True
- <small>Min</small>
- True
-
-
- 1
- 2
-
-
-
-
-
- True
- <small>Max</small>
- True
-
-
- 2
- 3
-
-
-
-
-
- True
- 1
- <small>Distance</small>
- True
-
-
- 2
- 3
-
-
-
-
-
- True
- True
- False
- True
- True
-
-
- 1
- 2
- 2
- 3
- GTK_EXPAND
-
-
-
-
- True
- 1
- <small>Time</small>
- True
-
-
- 3
- 4
-
-
-
-
-
- True
- True
- False
- True
- radiobuttonDistance
-
-
- 1
- 2
- 3
- 4
- GTK_EXPAND
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
- False
- 4
-
-
-
-
- True
-
-
- True
-
-
- 0
-
-
-
-
- Show Laps
- True
- True
- False
- True
-
-
-
- False
- False
- 5
- 1
-
-
-
-
- True
-
-
- 2
-
-
-
-
- False
- False
- 5
-
-
-
-
- True
-
-
- 6
-
-
-
-
- Reset Graph
- True
- True
- True
- True
- 1
-
-
-
- False
- False
- end
- 1
-
-
-
-
- False
- True
-
-
-
-
- True
-
-
-
-
-
- True
- True
-
+
diff --git a/man/pytrainer.1 b/man/pytrainer.1
index 821a26f6..ff43bd56 100644
--- a/man/pytrainer.1
+++ b/man/pytrainer.1
@@ -21,10 +21,6 @@ enable logging at warning level
enable logging at error level
.IP --valid
enable validation of files imported by plugins (details at info or debug logging level) - note plugin must support validation.
-.IP --oldgraph
-Turn off new graphing approach
-.IP --newgraph
-Deprecated option: turn on new graphing approach
.IP --confdir=CONF_DIR
Specify the directory where application configuration will be stored.
.IP --logtype=TYPE
diff --git a/plugins/garmin-tcxv2/garmin-tcxv2.py b/plugins/garmin-tcxv2/garmin-tcxv2.py
index e50bae4b..af5e8bf8 100644
--- a/plugins/garmin-tcxv2/garmin-tcxv2.py
+++ b/plugins/garmin-tcxv2/garmin-tcxv2.py
@@ -21,107 +21,107 @@
import os
from lxml import etree
from pytrainer.lib.xmlUtils import XMLParser
-from pytrainer.gui.dialogs import fileChooserDialog, guiFlush
from pytrainer.core.activity import Activity
from sqlalchemy.orm import exc
class garminTCXv2():
- def __init__(self, parent = None, validate=False):
- self.parent = parent
- self.pytrainer_main = parent.pytrainer_main
- self.tmpdir = self.pytrainer_main.profile.tmpdir
- self.data_path = os.path.dirname(__file__)
- self.validate = validate
- self.sport = self.getConfValue("Force_sport_to")
+ def __init__(self, parent = None, validate=False):
+ self.parent = parent
+ self.pytrainer_main = parent.pytrainer_main
+ self.tmpdir = self.pytrainer_main.profile.tmpdir
+ self.data_path = os.path.dirname(__file__)
+ self.validate = validate
+ self.sport = self.getConfValue("Force_sport_to")
- def getConfValue(self, confVar):
- info = XMLParser(self.data_path+"/conf.xml")
- code = info.getValue("pytrainer-plugin","plugincode")
- plugindir = self.pytrainer_main.profile.plugindir
- if not os.path.isfile(plugindir+"/"+code+"/conf.xml"):
- value = None
- else:
- info = XMLParser(plugindir+"/"+code+"/conf.xml")
- value = info.getValue("pytrainer-plugin",confVar)
- return value
+ def getConfValue(self, confVar):
+ info = XMLParser(self.data_path+"/conf.xml")
+ code = info.getValue("pytrainer-plugin","plugincode")
+ plugindir = self.pytrainer_main.profile.plugindir
+ if not os.path.isfile(plugindir+"/"+code+"/conf.xml"):
+ value = None
+ else:
+ info = XMLParser(plugindir+"/"+code+"/conf.xml")
+ value = info.getValue("pytrainer-plugin",confVar)
+ return value
- def run(self):
- logging.debug(">>")
- # able to select multiple files....
- selectedFiles = fileChooserDialog(title="Choose a TCX file (or files) to import", multiple=True).getFiles()
- guiFlush()
- importfiles = []
- if not selectedFiles: #Nothing selected
- return importfiles
- for filename in selectedFiles: #Multiple files
- if self.valid_input_file(filename): #TODO could consolidate tree generation here
- tree = etree.ElementTree(file=filename)
- #Possibly multiple entries in file
- activities = self.getActivities(tree)
- for activity in activities:
- if not self.inDatabase(activity):
- sport = self.getSport(activity)
- gpxfile = "%s/garmin-tcxv2-%d.gpx" % (self.tmpdir, len(importfiles))
- self.createGPXfile(gpxfile, activity)
- importfiles.append((gpxfile, sport))
- else:
- logging.debug("File:%s activity %d already in database. Skipping import." % (filename, activities.index(activity)) )
- else:
- logging.info("File %s failed validation" % (filename))
- logging.debug("<<")
- return importfiles
+ def run(self):
+ logging.debug(">>")
+ # able to select multiple files....
+ from pytrainer.gui.dialogs import fileChooserDialog, guiFlush
+ selectedFiles = fileChooserDialog(title="Choose a TCX file (or files) to import", multiple=True).getFiles()
+ guiFlush()
+ importfiles = []
+ if not selectedFiles: #Nothing selected
+ return importfiles
+ for filename in selectedFiles: #Multiple files
+ if self.valid_input_file(filename): #TODO could consolidate tree generation here
+ tree = etree.ElementTree(file=filename)
+ #Possibly multiple entries in file
+ activities = self.getActivities(tree)
+ for activity in activities:
+ if not self.inDatabase(activity):
+ sport = self.getSport(activity)
+ gpxfile = "%s/garmin-tcxv2-%d.gpx" % (self.tmpdir, len(importfiles))
+ self.createGPXfile(gpxfile, activity)
+ importfiles.append((gpxfile, sport))
+ else:
+ logging.debug("File:%s activity %d already in database. Skipping import." % (filename, activities.index(activity)) )
+ else:
+ logging.info("File %s failed validation" % (filename))
+ logging.debug("<<")
+ return importfiles
- def valid_input_file(self, filename):
- """ Function to validate input file if requested"""
- if not self.validate: #not asked to validate
- logging.debug("Not validating %s" % (filename) )
- return True
- else:
- xslfile = os.path.realpath(self.pytrainer_main.data_path)+ "/schemas/GarminTrainingCenterDatabase_v2.xsd"
- from pytrainer.lib.xmlValidation import xmlValidator
- validator = xmlValidator()
- return validator.validateXSL(filename, xslfile)
+ def valid_input_file(self, filename):
+ """ Function to validate input file if requested"""
+ if not self.validate: #not asked to validate
+ logging.debug("Not validating %s" % (filename) )
+ return True
+ else:
+ xslfile = os.path.realpath(self.pytrainer_main.data_path)+ "/schemas/GarminTrainingCenterDatabase_v2.xsd"
+ from pytrainer.lib.xmlValidation import xmlValidator
+ validator = xmlValidator()
+ return validator.validateXSL(filename, xslfile)
- def getActivities(self, tree):
- '''Function to return all activities in Garmin training center version 2 file
- '''
- root = tree.getroot()
- activities = root.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Activity")
- return activities
+ def getActivities(self, tree):
+ '''Function to return all activities in Garmin training center version 2 file
+ '''
+ root = tree.getroot()
+ activities = root.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Activity")
+ return activities
- def inDatabase(self, activity):
- #comparing date and start time (sport may have been changed in DB after import)
- time = self.detailsFromTCX(activity)
- try:
- self.pytrainer_main.ddbb.session.query(Activity).filter(Activity.date_time_utc == time).one()
- return True
- except exc.NoResultFound:
- return False
+ def inDatabase(self, activity):
+ #comparing date and start time (sport may have been changed in DB after import)
+ time = self.detailsFromTCX(activity)
+ try:
+ self.pytrainer_main.ddbb.session.query(Activity).filter(Activity.date_time_utc == time).one()
+ return True
+ except exc.NoResultFound:
+ return False
- def getSport(self, activity):
- #return sport from file or overide if present
- if self.sport:
- return self.sport
- #sportElement = activity.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Activity")
- try:
- sport = activity.get("Sport")
- except:
- sport = "import"
- return sport
+ def getSport(self, activity):
+ #return sport from file or overide if present
+ if self.sport:
+ return self.sport
+ #sportElement = activity.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Activity")
+ try:
+ sport = activity.get("Sport")
+ except:
+ sport = "import"
+ return sport
- def detailsFromTCX(self, activity):
- timeElement = activity.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Id")
- if timeElement is None:
- return None
- else:
- return timeElement.text
+ def detailsFromTCX(self, activity):
+ timeElement = activity.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Id")
+ if timeElement is None:
+ return None
+ else:
+ return timeElement.text
- def createGPXfile(self, gpxfile, activity):
- """ Function to transform a Garmin Training Center v2 Track to a valid GPX+ file
- """
- xslt_doc = etree.parse(self.data_path+"/translate.xsl")
- transform = etree.XSLT(xslt_doc)
- #xml_doc = etree.parse(filename)
- xml_doc = activity
- result_tree = transform(xml_doc)
- result_tree.write(gpxfile, xml_declaration=True, encoding='UTF-8')
+ def createGPXfile(self, gpxfile, activity):
+ """ Function to transform a Garmin Training Center v2 Track to a valid GPX+ file
+ """
+ xslt_doc = etree.parse(self.data_path+"/translate.xsl")
+ transform = etree.XSLT(xslt_doc)
+ #xml_doc = etree.parse(filename)
+ xml_doc = activity
+ result_tree = transform(xml_doc)
+ result_tree.write(gpxfile, xml_declaration=True, encoding='UTF-8')
diff --git a/pyproject.toml b/pyproject.toml
index 79d835f8..8dae10da 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -32,6 +32,7 @@ repository = "https://github.com/pytrainer"
[project.optional-dependencies]
mysql = ["mysqlclient"]
postgresql = ["psycopg2"]
+gui = ["pycairo", "pygobject"]
[build-system]
requires = ["setuptools>=45", "setuptools_scm>=8"]
diff --git a/pytrainer/__main__.py b/pytrainer/__main__.py
index af263e52..94dc17d2 100644
--- a/pytrainer/__main__.py
+++ b/pytrainer/__main__.py
@@ -17,7 +17,6 @@ def get_options():
log_level=logging.WARNING,
validate=False,
equip=False,
- newgraph=True,
conf_dir=None,
log_type="file",
)
@@ -58,18 +57,6 @@ def get_options():
dest="validate",
help="enable validation of files imported by plugins (details at info or debug logging level) - note plugin must support validation.",
)
- parser.add_option(
- "--oldgraph",
- action="store_false",
- dest="newgraph",
- help="Turn off new graphing approach",
- )
- parser.add_option(
- "--newgraph",
- action="store_true",
- dest="newgraph",
- help="Deprecated Option: Turn on new graphing approach",
- )
parser.add_option(
"--confdir",
dest="conf_dir",
diff --git a/pytrainer/extensions/mapviewer.py b/pytrainer/extensions/mapviewer.py
index fcec7c71..72801115 100644
--- a/pytrainer/extensions/mapviewer.py
+++ b/pytrainer/extensions/mapviewer.py
@@ -20,7 +20,7 @@
import gi
try:
- gi.require_version('WebKit2', '4.0')
+ gi.require_version('WebKit2', '4.1')
except ValueError:
pass
diff --git a/pytrainer/gui/popupmenu.py b/pytrainer/gui/popupmenu.py
index d080119a..2daa7be7 100644
--- a/pytrainer/gui/popupmenu.py
+++ b/pytrainer/gui/popupmenu.py
@@ -19,24 +19,31 @@
from gi.repository import Gtk
+
class PopupMenu(Gtk.Menu):
def __init__(self, data_path = None, parent = None):
super(PopupMenu, self).__init__()
self.windowmain = parent
- edit_record = Gtk.ImageMenuItem(Gtk.STOCK_EDIT)
- edit_record.set_label(_("Edit Record"))
+ edit_record = Gtk.ImageMenuItem(
+ image=Gtk.Image.new_from_stock(Gtk.STOCK_EDIT, Gtk.IconSize.MENU),
+ label=_("Edit Record"),
+ )
edit_record.connect("activate", self.on_editrecord_activate)
self.attach(edit_record, 0, 1, 0, 1)
- show_graph = Gtk.ImageMenuItem(Gtk.STOCK_FIND)
- show_graph.set_label(_("Show graph in classic view"))
+ show_graph = Gtk.ImageMenuItem(
+ image=Gtk.Image.new_from_stock(Gtk.STOCK_FIND, Gtk.IconSize.MENU),
+ label=_("Show graph in classic view"),
+ )
show_graph.connect("activate", self.on_showclassic_activate)
self.attach(show_graph, 0, 1, 1, 2)
self.attach(Gtk.SeparatorMenuItem(), 0, 1, 2, 3)
- remove_record = Gtk.ImageMenuItem(Gtk.STOCK_DELETE)
- remove_record.set_label(_("Delete"))
+ remove_record = Gtk.ImageMenuItem(
+ image=Gtk.Image.new_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.MENU),
+ label=_("Delete"),
+ )
remove_record.connect("activate", self.on_remove_activate)
self.attach(remove_record, 0, 1, 3, 4)
-
+
def show(self,id_record,event_button, time, date=None):
self.id_record = id_record
self.date = date
diff --git a/pytrainer/gui/windowmain.py b/pytrainer/gui/windowmain.py
index 938e4952..f995dee1 100644
--- a/pytrainer/gui/windowmain.py
+++ b/pytrainer/gui/windowmain.py
@@ -257,7 +257,7 @@ def runExtension(self,widget,widget2,extension):
def createGraphs(self):
logging.debug(">>")
- self.drawarearecord = RecordGraph(self.record_graph_vbox, self.window1, self.record_combovalue, self.record_combovalue2, self.btnShowLaps, self.tableConfigY1, pytrainer_main=self.pytrainer_main)
+ self.drawarearecord = RecordGraph(self.record_graph_vbox, self.window1, pytrainer_main=self.pytrainer_main)
self.drawareaheartrate = HeartRateGraph([self.heartrate_vbox, self.heartrate_vbox2, self.heartrate_vbox3], self.window1, pytrainer_main=self.pytrainer_main)
self.day_vbox.hide()
sports = self._sport_service.get_all_sports()
@@ -550,175 +550,159 @@ def actualize_recordgraph(self,activity):
self.record_list = activity.tracks
self.laps = activity.laps
if activity.gpx_file is not None:
- if not self.pytrainer_main.startup_options.newgraph:
- logging.debug("Using the original graphing")
- logging.debug("Activity has GPX data")
- #Show drop down boxes
- self.hbox30.show()
- #Hide new graph details
- self.graph_data_hbox.hide()
- self.hboxGraphOptions.hide()
- #Enable graph
- self.record_vbox.set_sensitive(1)
- self.drawarearecord.drawgraph(self.record_list,self.laps)
+ self.graph_data_hbox.hide()
+ #Enable graph
+ self.record_vbox.set_sensitive(1)
+ #Create a frame showing data available for graphing
+ #Remove existing frames
+ for child in self.graph_data_hbox.get_children():
+ if isinstance(child, Gtk.Frame):
+ self.graph_data_hbox.remove(child)
+ #Build frames and vboxs to hold checkbuttons
+ xFrame = Gtk.Frame(label=_("Show on X Axis"))
+ y1Frame = Gtk.Frame(label=_("Show on Y1 Axis"))
+ y2Frame = Gtk.Frame(label=_("Show on Y2 Axis"))
+ limitsFrame = Gtk.Frame(label=_("Axis Limits"))
+ xvbox = Gtk.VBox()
+ y1box = Gtk.Table()
+ y2box = Gtk.Table()
+ limitsbox = Gtk.Table()
+ #Populate X axis data
+ #Create x axis items
+ xdistancebutton = Gtk.RadioButton(label=_("Distance"))
+ xtimebutton = Gtk.RadioButton(group=xdistancebutton, label=_("Time"))
+ xlapsbutton = Gtk.CheckButton(label=_("Laps"))
+ y1gridbutton = Gtk.CheckButton(label=_("Left Axis Grid"))
+ y2gridbutton = Gtk.CheckButton(label=_("Right Axis Grid"))
+ xgridbutton = Gtk.CheckButton(label=_("X Axis Grid"))
+ #Set state of buttons
+ if activity.x_axis == "distance":
+ xdistancebutton.set_active(True)
+ elif activity.x_axis == "time":
+ xtimebutton.set_active(True)
+ xlapsbutton.set_active(activity.show_laps)
+ y1gridbutton.set_active(activity.y1_grid)
+ y2gridbutton.set_active(activity.y2_grid)
+ xgridbutton.set_active(activity.x_grid)
+ #Connect handlers to buttons
+ xdistancebutton.connect("toggled", self.on_xaxischange, "distance", activity)
+ xtimebutton.connect("toggled", self.on_xaxischange, "time", activity)
+ xlapsbutton.connect("toggled", self.on_xlapschange, activity)
+ y1gridbutton.connect("toggled", self.on_gridchange, "y1", activity)
+ y2gridbutton.connect("toggled", self.on_gridchange, "y2", activity)
+ xgridbutton.connect("toggled", self.on_gridchange, "x", activity)
+ #Add buttons to frame
+ xvbox.pack_start(xdistancebutton, False, True, 0)
+ xvbox.pack_start(xtimebutton, False, True, 0)
+ xvbox.pack_start(xlapsbutton, False, True, 0)
+ xvbox.pack_start(y1gridbutton, False, True, 0)
+ xvbox.pack_start(y2gridbutton, False, True, 0)
+ xvbox.pack_start(xgridbutton, False, True, 0)
+ xFrame.add(xvbox)
+
+ #Populate axis limits frame
+ #TODO Need to change these to editable objects and redraw graphs if changed....
+ #Create labels etc
+ minlabel = Gtk.Label(label="Min ")
+ minlabel.set_use_markup(True)
+ maxlabel = Gtk.Label(label="Max ")
+ maxlabel.set_use_markup(True)
+ xlimlabel = Gtk.Label(label="X")
+ limits = {}
+ xminlabel = Gtk.Entry(max_length=10)
+ xmaxlabel = Gtk.Entry(max_length=10)
+ limits['xminlabel'] = xminlabel
+ limits['xmaxlabel'] = xmaxlabel
+ xminlabel.set_width_chars(5)
+ xminlabel.set_alignment(1.0)
+ xmaxlabel.set_width_chars(5)
+ xmaxlabel.set_alignment(1.0)
+ y1limlabel = Gtk.Label(label="Y1")
+ y1minlabel = Gtk.Entry(max_length=10)
+ y1maxlabel = Gtk.Entry(max_length=10)
+ limits['y1minlabel'] = y1minlabel
+ limits['y1maxlabel'] = y1maxlabel
+ y1minlabel.set_width_chars(5)
+ y1minlabel.set_alignment(1.0)
+ y1maxlabel.set_width_chars(5)
+ y1maxlabel.set_alignment(1.0)
+ y2limlabel = Gtk.Label(label="Y2")
+ y2minlabel = Gtk.Entry(max_length=10)
+ y2maxlabel = Gtk.Entry(max_length=10)
+ limits['y2minlabel'] = y2minlabel
+ limits['y2maxlabel'] = y2maxlabel
+ y2minlabel.set_width_chars(5)
+ y2minlabel.set_alignment(1.0)
+ y2maxlabel.set_width_chars(5)
+ y2maxlabel.set_alignment(1.0)
+ resetbutton = Gtk.Button(_('Reset Limits'))
+ resetbutton.connect("clicked", self.on_setlimits, activity, True, None)
+ setbutton = Gtk.Button(_('Set Limits'))
+ setbutton.connect("clicked", self.on_setlimits, activity, False, limits)
+ #Add labels etc to table
+ limitsbox.attach(minlabel, 1, 2, 0, 1, yoptions=Gtk.AttachOptions.SHRINK)
+ limitsbox.attach(maxlabel, 2, 3, 0, 1, yoptions=Gtk.AttachOptions.SHRINK)
+ limitsbox.attach(xlimlabel, 0, 1, 1, 2, yoptions=Gtk.AttachOptions.SHRINK)
+ limitsbox.attach(xminlabel, 1, 2, 1, 2, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
+ limitsbox.attach(xmaxlabel, 2, 3, 1, 2, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
+ limitsbox.attach(y1limlabel, 0, 1, 2, 3, yoptions=Gtk.AttachOptions.SHRINK)
+ limitsbox.attach(y1minlabel, 1, 2, 2, 3, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
+ limitsbox.attach(y1maxlabel, 2, 3, 2, 3, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
+ limitsbox.attach(y2limlabel, 0, 1, 3, 4, yoptions=Gtk.AttachOptions.SHRINK)
+ limitsbox.attach(y2minlabel, 1, 2, 3, 4, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
+ limitsbox.attach(y2maxlabel, 2, 3, 3, 4, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
+ limitsbox.attach(setbutton, 0, 3, 4, 5, yoptions=Gtk.AttachOptions.SHRINK)
+ limitsbox.attach(resetbutton, 0, 3, 5, 6, yoptions=Gtk.AttachOptions.SHRINK)
+ limitsFrame.add(limitsbox)
+
+ row = 0
+ if activity.x_axis == "distance":
+ data = activity.distance_data
+ elif activity.x_axis == "time":
+ data = activity.time_data
else:
- #Still just test code....
- logging.debug("Using the new TEST graphing approach")
- #Hide current drop down boxes
- self.hbox30.hide()
- self.graph_data_hbox.hide()
- #Enable graph
- self.record_vbox.set_sensitive(1)
- #Create a frame showing data available for graphing
- #Remove existing frames
- for child in self.graph_data_hbox.get_children():
- if isinstance(child, Gtk.Frame):
- self.graph_data_hbox.remove(child)
- #Build frames and vboxs to hold checkbuttons
- xFrame = Gtk.Frame(label=_("Show on X Axis"))
- y1Frame = Gtk.Frame(label=_("Show on Y1 Axis"))
- y2Frame = Gtk.Frame(label=_("Show on Y2 Axis"))
- limitsFrame = Gtk.Frame(label=_("Axis Limits"))
- xvbox = Gtk.VBox()
- y1box = Gtk.Table()
- y2box = Gtk.Table()
- limitsbox = Gtk.Table()
- #Populate X axis data
- #Create x axis items
- xdistancebutton = Gtk.RadioButton(label=_("Distance"))
- xtimebutton = Gtk.RadioButton(group=xdistancebutton, label=_("Time"))
- xlapsbutton = Gtk.CheckButton(label=_("Laps"))
- y1gridbutton = Gtk.CheckButton(label=_("Left Axis Grid"))
- y2gridbutton = Gtk.CheckButton(label=_("Right Axis Grid"))
- xgridbutton = Gtk.CheckButton(label=_("X Axis Grid"))
- #Set state of buttons
- if activity.x_axis == "distance":
- xdistancebutton.set_active(True)
- elif activity.x_axis == "time":
- xtimebutton.set_active(True)
- xlapsbutton.set_active(activity.show_laps)
- y1gridbutton.set_active(activity.y1_grid)
- y2gridbutton.set_active(activity.y2_grid)
- xgridbutton.set_active(activity.x_grid)
- #Connect handlers to buttons
- xdistancebutton.connect("toggled", self.on_xaxischange, "distance", activity)
- xtimebutton.connect("toggled", self.on_xaxischange, "time", activity)
- xlapsbutton.connect("toggled", self.on_xlapschange, activity)
- y1gridbutton.connect("toggled", self.on_gridchange, "y1", activity)
- y2gridbutton.connect("toggled", self.on_gridchange, "y2", activity)
- xgridbutton.connect("toggled", self.on_gridchange, "x", activity)
- #Add buttons to frame
- xvbox.pack_start(xdistancebutton, False, True, 0)
- xvbox.pack_start(xtimebutton, False, True, 0)
- xvbox.pack_start(xlapsbutton, False, True, 0)
- xvbox.pack_start(y1gridbutton, False, True, 0)
- xvbox.pack_start(y2gridbutton, False, True, 0)
- xvbox.pack_start(xgridbutton, False, True, 0)
- xFrame.add(xvbox)
-
- #Populate axis limits frame
- #TODO Need to change these to editable objects and redraw graphs if changed....
- #Create labels etc
- minlabel = Gtk.Label(label="Min ")
- minlabel.set_use_markup(True)
- maxlabel = Gtk.Label(label="Max ")
- maxlabel.set_use_markup(True)
- xlimlabel = Gtk.Label(label="X")
- limits = {}
- xminlabel = Gtk.Entry(max_length=10)
- xmaxlabel = Gtk.Entry(max_length=10)
- limits['xminlabel'] = xminlabel
- limits['xmaxlabel'] = xmaxlabel
- xminlabel.set_width_chars(5)
- xminlabel.set_alignment(1.0)
- xmaxlabel.set_width_chars(5)
- xmaxlabel.set_alignment(1.0)
- y1limlabel = Gtk.Label(label="Y1")
- y1minlabel = Gtk.Entry(max_length=10)
- y1maxlabel = Gtk.Entry(max_length=10)
- limits['y1minlabel'] = y1minlabel
- limits['y1maxlabel'] = y1maxlabel
- y1minlabel.set_width_chars(5)
- y1minlabel.set_alignment(1.0)
- y1maxlabel.set_width_chars(5)
- y1maxlabel.set_alignment(1.0)
- y2limlabel = Gtk.Label(label="Y2")
- y2minlabel = Gtk.Entry(max_length=10)
- y2maxlabel = Gtk.Entry(max_length=10)
- limits['y2minlabel'] = y2minlabel
- limits['y2maxlabel'] = y2maxlabel
- y2minlabel.set_width_chars(5)
- y2minlabel.set_alignment(1.0)
- y2maxlabel.set_width_chars(5)
- y2maxlabel.set_alignment(1.0)
- resetbutton = Gtk.Button(_('Reset Limits'))
- resetbutton.connect("clicked", self.on_setlimits, activity, True, None)
- setbutton = Gtk.Button(_('Set Limits'))
- setbutton.connect("clicked", self.on_setlimits, activity, False, limits)
- #Add labels etc to table
- limitsbox.attach(minlabel, 1, 2, 0, 1, yoptions=Gtk.AttachOptions.SHRINK)
- limitsbox.attach(maxlabel, 2, 3, 0, 1, yoptions=Gtk.AttachOptions.SHRINK)
- limitsbox.attach(xlimlabel, 0, 1, 1, 2, yoptions=Gtk.AttachOptions.SHRINK)
- limitsbox.attach(xminlabel, 1, 2, 1, 2, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
- limitsbox.attach(xmaxlabel, 2, 3, 1, 2, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
- limitsbox.attach(y1limlabel, 0, 1, 2, 3, yoptions=Gtk.AttachOptions.SHRINK)
- limitsbox.attach(y1minlabel, 1, 2, 2, 3, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
- limitsbox.attach(y1maxlabel, 2, 3, 2, 3, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
- limitsbox.attach(y2limlabel, 0, 1, 3, 4, yoptions=Gtk.AttachOptions.SHRINK)
- limitsbox.attach(y2minlabel, 1, 2, 3, 4, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
- limitsbox.attach(y2maxlabel, 2, 3, 3, 4, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
- limitsbox.attach(setbutton, 0, 3, 4, 5, yoptions=Gtk.AttachOptions.SHRINK)
- limitsbox.attach(resetbutton, 0, 3, 5, 6, yoptions=Gtk.AttachOptions.SHRINK)
- limitsFrame.add(limitsbox)
-
- row = 0
- if activity.x_axis == "distance":
- data = activity.distance_data
- elif activity.x_axis == "time":
- data = activity.time_data
+ logging.error("x axis is unknown")
+ #Populate Y axis data
+ for graphdata in sorted(data.keys()):
+ #First Y axis...
+ #Create button
+ y1button = Gtk.CheckButton(label=data[graphdata].title)
+ #Make button active if this data is to be displayed...
+ y1button.set_active(data[graphdata].show_on_y1)
+ #Connect handler for toggle state changes
+ y1button.connect("toggled", self.on_y1change, y1box, graphdata, activity)
+ #Attach button to container
+ y1box.attach(y1button, 0, 1, row, row+1, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL)
+ if data[graphdata].linecolor is not None:
+ #Create a color choser
+ y1color = Gtk.ColorButton()
+ #Set color to current activity color
+ _color = Gdk.color_parse(data[graphdata].linecolor)
+ y1color.set_color(_color)
+ #Connect handler for color state changes
+ y1color.connect("color-set", self.on_y1colorchange, y1box, graphdata, activity)
+ #Attach to container
+ y1box.attach(y1color, 1, 2, row, row+1)
else:
- logging.error("x axis is unknown")
- #Populate Y axis data
- for graphdata in sorted(data.keys()):
- #First Y axis...
- #Create button
- y1button = Gtk.CheckButton(label=data[graphdata].title)
- #Make button active if this data is to be displayed...
- y1button.set_active(data[graphdata].show_on_y1)
- #Connect handler for toggle state changes
- y1button.connect("toggled", self.on_y1change, y1box, graphdata, activity)
- #Attach button to container
- y1box.attach(y1button, 0, 1, row, row+1, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL)
- if data[graphdata].linecolor is not None:
- #Create a color choser
- y1color = Gtk.ColorButton()
- #Set color to current activity color
- _color = Gdk.color_parse(data[graphdata].linecolor)
- y1color.set_color(_color)
- #Connect handler for color state changes
- y1color.connect("color-set", self.on_y1colorchange, y1box, graphdata, activity)
- #Attach to container
- y1box.attach(y1color, 1, 2, row, row+1)
- else:
- blanklabel = Gtk.Label(label="")
- y1box.attach(blanklabel, 1, 2, row, row+1)
-
- #Second Y axis
- y2button = Gtk.CheckButton(label=data[graphdata].title)
- y2button.set_active(data[graphdata].show_on_y2)
- y2button.connect("toggled", self.on_y2change, y2box, graphdata, activity)
- y2box.attach(y2button, 0, 1, row, row+1, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL)
- if data[graphdata].y2linecolor is not None:
- y2color = Gtk.ColorButton()
- _color = Gdk.color_parse(data[graphdata].y2linecolor)
- y2color.set_color(_color)
- y2color.connect("color-set", self.on_y2colorchange, y2box, graphdata, activity)
- #Attach to container
- y2box.attach(y2color, 1, 2, row, row+1)
- else:
- blanklabel = Gtk.Label(label="")
- y2box.attach(blanklabel, 1, 2, row, row+1)
- row += 1
+ blanklabel = Gtk.Label(label="")
+ y1box.attach(blanklabel, 1, 2, row, row+1)
+
+ #Second Y axis
+ y2button = Gtk.CheckButton(label=data[graphdata].title)
+ y2button.set_active(data[graphdata].show_on_y2)
+ y2button.connect("toggled", self.on_y2change, y2box, graphdata, activity)
+ y2box.attach(y2button, 0, 1, row, row+1, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL)
+ if data[graphdata].y2linecolor is not None:
+ y2color = Gtk.ColorButton()
+ _color = Gdk.color_parse(data[graphdata].y2linecolor)
+ y2color.set_color(_color)
+ y2color.connect("color-set", self.on_y2colorchange, y2box, graphdata, activity)
+ #Attach to container
+ y2box.attach(y2color, 1, 2, row, row+1)
+ else:
+ blanklabel = Gtk.Label(label="")
+ y2box.attach(blanklabel, 1, 2, row, row+1)
+ row += 1
y1Frame.add(y1box)
y2Frame.add(y2box)
@@ -759,8 +743,6 @@ def actualize_recordgraph(self,activity):
self.buttonGraphHideOptions.show()
else:
logging.debug("Activity has no GPX data")
- #Show drop down boxes
- self.hbox30.show()
#Hide new graph details
self.graph_data_hbox.hide()
self.hboxGraphOptions.hide()
@@ -1389,7 +1371,7 @@ def create_menulist(self,columns):
if 'visible' in column_dict and not column_dict['visible']:
pass
else:
- item = Gtk.CheckMenuItem(column_dict['name'])
+ item = Gtk.CheckMenuItem(label=column_dict['name'])
#self.lsa_searchoption.append_text(name)
item.connect("button_press_event", self.on_menulistview_activate, i)
self.menulistviewOptions.append(item)
@@ -1586,18 +1568,6 @@ def on_window1_configure_event(self, widget, event):
#print event # resize event
self.size = self.window1.get_size()
- def on_buttonShowOptions_clicked(self, widget):
- position_set = self.hpaned1.get_property('position-set')
- if position_set:
- #Currently not showing options - show them
- self.hpaned1.set_property('position-set', False)
- self.buttonShowOptions.set_tooltip_text(_('Hide graph display options') )
- else:
- #Hide options
- self.hpaned1.set_position(0)
- self.buttonShowOptions.set_tooltip_text(_('Show graph display options') )
- logging.debug('Position set: %s', self.hpaned1.get_property('position-set'))
-
def on_buttonGraphHideOptions_clicked(self, widget):
logging.debug('on_buttonGraphHideOptions_clicked')
self.buttonGraphHideOptions.hide()
@@ -1632,39 +1602,6 @@ def on_comboMapLineType_changed(self, widget):
logging.debug('on_comboMapLineType_changed %s = %s', widget.get_name(), widget.get_active())
self.parent.refreshMapView()
- def on_hpaned1_move_handle(self, widget):
- logging.debug("Handler %s", widget)
-
- def on_spinbuttonY1_value_changed(self, widget):
- y1min = self.spinbuttonY1Min.get_value()
- y1max = self.spinbuttonY1Max.get_value()
- #Check to see if the min and max have the same...
- if y1min == y1max:
- if widget.get_name() == "spinbuttonY1Min": #User was changing the min spinbutton, so move max up
- y1max += 1
- else: #Move min down
- y1min -= 1
- self.y1_limits=(y1min, y1max)
- self.zoom_graph(y1limits=self.y1_limits, y1color=self.y1_color, y1_linewidth=self.y1_linewidth)
-
- def on_buttonResetGraph_clicked(self, widget):
- #self.zoom_graph()
- #Reset stored values
- self.y1_limits = None
- self.y1_color = None
- self.y1_linewidth = 1
- self.zoom_graph()
-
- def on_colorbuttonY1LineColor_color_set(self, widget):
- y1color = widget.get_color()
- cs = y1color.to_string()
- self.y1_color = cs[0:3] + cs[5:7] + cs[9:11]
- self.drawarearecord.drawgraph(self.record_list,self.laps, y1limits=self.y1_limits, y1color=self.y1_color, y1_linewidth=self.y1_linewidth)
-
- def on_spinbuttonY1LineWeight_value_changed(self, widget):
- self.y1_linewidth = self.spinbuttonY1LineWeight.get_value_as_int()
- self.drawarearecord.drawgraph(self.record_list,self.laps, y1limits=self.y1_limits, y1color=self.y1_color, y1_linewidth=self.y1_linewidth)
-
def on_edit_clicked(self,widget):
selected,iter = self.recordTreeView.get_selection().get_selected()
id_record = selected.get_value(iter,0)
@@ -1742,10 +1679,6 @@ def on_hidemap_clicked(self,widget):
self.maparea.hide()
self.infoarea.show()
- def on_btnShowLaps_toggled(self,widget):
- logging.debug("--")
- self.parent.refreshGraphView(self.selected_view)
-
def on_day_combovalue_changed(self,widget):
logging.debug("--")
self.parent.refreshGraphView(self.selected_view)
diff --git a/pytrainer/gui/windowprofile.py b/pytrainer/gui/windowprofile.py
index 67c06285..30a4ff28 100644
--- a/pytrainer/gui/windowprofile.py
+++ b/pytrainer/gui/windowprofile.py
@@ -224,13 +224,7 @@ def init_params_tab(self):
self.checkbuttonValidate.set_active(True)
else:
self.checkbuttonValidate.set_active(False)
-
- #Show if new graph activated
- if self.pytrainer_main.startup_options.newgraph:
- self.checkbuttonNewGraph.set_active(True)
- else:
- self.checkbuttonNewGraph.set_active(False)
-
+
def on_comboboxLogLevel_changed(self, widget):
active = self.comboboxLogLevel.get_active()
if active == 1:
@@ -254,15 +248,7 @@ def on_checkbuttonValidate_toggled(self, widget):
else:
logging.debug("Validate deactivated")
self.pytrainer_main.startup_options.validate = False
-
- def on_checkbuttonNewGraph_toggled(self, widget):
- if self.checkbuttonNewGraph.get_active():
- logging.debug("NewGraph activated")
- self.pytrainer_main.startup_options.newgraph = True
- else:
- logging.debug("NewGraph deactivated")
- self.pytrainer_main.startup_options.newgraph = False
-
+
def on_accept_clicked(self,widget):
self.saveOptions()
self.close_window()
diff --git a/pytrainer/platform.py b/pytrainer/platform.py
index 2fd4637b..cc2e57c6 100644
--- a/pytrainer/platform.py
+++ b/pytrainer/platform.py
@@ -58,7 +58,7 @@ def _get_data_path(self):
def _checkpath(fname):
return os.path.exists(os.path.join(base_path, fname))
- if all(map(_checkpath, ("INSTALL", "setup.py", "pytrainer/main.py", "locale"))):
+ if all(map(_checkpath, ("pyproject.toml", "pytrainer/main.py", "locale"))):
return base_path + "/"
else:
return os.path.join(sys.prefix, "share/pytrainer/")
diff --git a/pytrainer/plugins.py b/pytrainer/plugins.py
index b42c5ace..5dfdc2f4 100644
--- a/pytrainer/plugins.py
+++ b/pytrainer/plugins.py
@@ -22,111 +22,111 @@
from .lib.xmlUtils import XMLParser
-from .gui.windowplugins import WindowPlugins
class Plugins:
- def __init__(self, data_path = None, parent = None):
- self.data_path=data_path
- self.pytrainer_main = parent
-
- def getActivePlugins(self):
- retorno = []
- for plugin in self.getPluginsList():
- if self.getPluginInfo(plugin[0])[2] == "1":
- retorno.append(plugin[0])
- return retorno
-
- def loadPlugin(self,plugin):
- logging.debug('>>')
- info = XMLParser(plugin+"/conf.xml")
- button = info.getValue("pytrainer-plugin","pluginbutton")
- name = info.getValue("pytrainer-plugin","name")
- logging.info('Loading plugin '+name)
- logging.debug('<<')
- return button,plugin
-
- def importClass(self, pathPlugin):
- logging.debug('>>')
- info = XMLParser(pathPlugin+"/conf.xml")
- #import plugin
- plugin_dir = os.path.realpath(pathPlugin)
- plugin_filename = info.getValue("pytrainer-plugin","executable")
- plugin_classname = info.getValue("pytrainer-plugin","plugincode")
- logging.debug("Plugin Filename: " + plugin_filename )
- logging.debug("Plugin Classname: " + plugin_classname)
- sys.path.insert(0, plugin_dir)
- module = __import__(plugin_filename)
- pluginMain = getattr(module, plugin_classname)
- logging.debug('<<')
- #Only validate files if enabled at startup
- if self.pytrainer_main.startup_options.validate:
- validate_inputfiles=True
- logging.info("validating plugin input files enabled")
- else:
- validate_inputfiles=False
- return pluginMain(self, validate_inputfiles)
-
- def managePlugins(self):
- pluginsList = self.getPluginsList()
- windowplugins = WindowPlugins(self.data_path, self)
- windowplugins.setList(pluginsList)
- windowplugins.run()
-
- def getPluginsList(self):
- pluginsdir = self.data_path+"/plugins"
- pluginsList = []
- for plugin in os.listdir(pluginsdir):
- pluginxmlfile = pluginsdir+"/"+plugin+"/conf.xml"
- if os.path.isfile(pluginxmlfile):
- plugininfo = XMLParser(pluginxmlfile)
- name = plugininfo.getValue("pytrainer-plugin","name")
- description = plugininfo.getValue("pytrainer-plugin","description")
- pluginsList.append((pluginsdir+"/"+plugin,name,description))
- pluginsList.sort()
- return pluginsList
-
- def getPluginInfo(self,pathPlugin):
- info = XMLParser(pathPlugin+"/conf.xml")
- name = info.getValue("pytrainer-plugin","name")
- description = info.getValue("pytrainer-plugin","description")
- code = info.getValue("pytrainer-plugin","plugincode")
- plugindir = self.pytrainer_main.profile.plugindir
- if not os.path.isfile(plugindir+"/"+code+"/conf.xml"):
- status = 0
- else:
- info = XMLParser(plugindir+"/"+code+"/conf.xml")
- status = info.getValue("pytrainer-plugin","status")
- return name,description,status
-
- def getPluginConfParams(self,pathPlugin):
- info = XMLParser(pathPlugin+"/conf.xml")
- code = info.getValue("pytrainer-plugin","plugincode")
- plugindir = self.pytrainer_main.profile.plugindir
- if not os.path.isfile(plugindir+"/"+code+"/conf.xml"):
- params = info.getAllValues("conf-values")
- params.append(("status","0"))
- else:
- prefs = info.getAllValues("conf-values")
- prefs.append(("status","0"))
- info = XMLParser(plugindir+"/"+code+"/conf.xml")
- params = []
- for pref in prefs:
- params.append((pref[0],info.getValue("pytrainer-plugin",pref[0])))
- return params
-
- def setPluginConfParams(self,pathPlugin,savedOptions):
- info = XMLParser(pathPlugin+"/conf.xml")
- code = info.getValue("pytrainer-plugin","plugincode")
- plugindir = self.pytrainer_main.profile.plugindir+"/"+code
- if not os.path.isdir(plugindir):
- os.mkdir(plugindir)
- if not os.path.isfile(plugindir+"/conf.xml"):
- if ("status", "1") not in savedOptions:
- savedOptions.append(("status","0"))
- info = XMLParser(plugindir+"/conf.xml")
- info.createXMLFile("pytrainer-plugin",savedOptions)
-
- def getCodeConfValue(self,code,value):
- plugindir = self.pytrainer_main.profile.plugindir
- info = XMLParser(plugindir+"/"+code+"/conf.xml")
- return info.getValue("pytrainer-plugin",value)
+ def __init__(self, data_path = None, parent = None):
+ self.data_path=data_path
+ self.pytrainer_main = parent
+
+ def getActivePlugins(self):
+ retorno = []
+ for plugin in self.getPluginsList():
+ if self.getPluginInfo(plugin[0])[2] == "1":
+ retorno.append(plugin[0])
+ return retorno
+
+ def loadPlugin(self,plugin):
+ logging.debug('>>')
+ info = XMLParser(plugin+"/conf.xml")
+ button = info.getValue("pytrainer-plugin","pluginbutton")
+ name = info.getValue("pytrainer-plugin","name")
+ logging.info('Loading plugin '+name)
+ logging.debug('<<')
+ return button,plugin
+
+ def importClass(self, pathPlugin):
+ logging.debug('>>')
+ info = XMLParser(pathPlugin+"/conf.xml")
+ #import plugin
+ plugin_dir = os.path.realpath(pathPlugin)
+ plugin_filename = info.getValue("pytrainer-plugin","executable")
+ plugin_classname = info.getValue("pytrainer-plugin","plugincode")
+ logging.debug("Plugin Filename: " + plugin_filename )
+ logging.debug("Plugin Classname: " + plugin_classname)
+ sys.path.insert(0, plugin_dir)
+ module = __import__(plugin_filename)
+ pluginMain = getattr(module, plugin_classname)
+ logging.debug('<<')
+ #Only validate files if enabled at startup
+ if self.pytrainer_main.startup_options.validate:
+ validate_inputfiles=True
+ logging.info("validating plugin input files enabled")
+ else:
+ validate_inputfiles=False
+ return pluginMain(self, validate_inputfiles)
+
+ def managePlugins(self):
+ pluginsList = self.getPluginsList()
+ from .gui.windowplugins import WindowPlugins
+ windowplugins = WindowPlugins(self.data_path, self)
+ windowplugins.setList(pluginsList)
+ windowplugins.run()
+
+ def getPluginsList(self):
+ pluginsdir = self.data_path+"/plugins"
+ pluginsList = []
+ for plugin in os.listdir(pluginsdir):
+ pluginxmlfile = pluginsdir+"/"+plugin+"/conf.xml"
+ if os.path.isfile(pluginxmlfile):
+ plugininfo = XMLParser(pluginxmlfile)
+ name = plugininfo.getValue("pytrainer-plugin","name")
+ description = plugininfo.getValue("pytrainer-plugin","description")
+ pluginsList.append((pluginsdir+"/"+plugin,name,description))
+ pluginsList.sort()
+ return pluginsList
+
+ def getPluginInfo(self,pathPlugin):
+ info = XMLParser(pathPlugin+"/conf.xml")
+ name = info.getValue("pytrainer-plugin","name")
+ description = info.getValue("pytrainer-plugin","description")
+ code = info.getValue("pytrainer-plugin","plugincode")
+ plugindir = self.pytrainer_main.profile.plugindir
+ if not os.path.isfile(plugindir+"/"+code+"/conf.xml"):
+ status = 0
+ else:
+ info = XMLParser(plugindir+"/"+code+"/conf.xml")
+ status = info.getValue("pytrainer-plugin","status")
+ return name,description,status
+
+ def getPluginConfParams(self,pathPlugin):
+ info = XMLParser(pathPlugin+"/conf.xml")
+ code = info.getValue("pytrainer-plugin","plugincode")
+ plugindir = self.pytrainer_main.profile.plugindir
+ if not os.path.isfile(plugindir+"/"+code+"/conf.xml"):
+ params = info.getAllValues("conf-values")
+ params.append(("status","0"))
+ else:
+ prefs = info.getAllValues("conf-values")
+ prefs.append(("status","0"))
+ info = XMLParser(plugindir+"/"+code+"/conf.xml")
+ params = []
+ for pref in prefs:
+ params.append((pref[0],info.getValue("pytrainer-plugin",pref[0])))
+ return params
+
+ def setPluginConfParams(self,pathPlugin,savedOptions):
+ info = XMLParser(pathPlugin+"/conf.xml")
+ code = info.getValue("pytrainer-plugin","plugincode")
+ plugindir = self.pytrainer_main.profile.plugindir+"/"+code
+ if not os.path.isdir(plugindir):
+ os.mkdir(plugindir)
+ if not os.path.isfile(plugindir+"/conf.xml"):
+ if ("status", "1") not in savedOptions:
+ savedOptions.append(("status","0"))
+ info = XMLParser(plugindir+"/conf.xml")
+ info.createXMLFile("pytrainer-plugin",savedOptions)
+
+ def getCodeConfValue(self,code,value):
+ plugindir = self.pytrainer_main.profile.plugindir
+ info = XMLParser(plugindir+"/"+code+"/conf.xml")
+ return info.getValue("pytrainer-plugin",value)
diff --git a/pytrainer/recordgraph.py b/pytrainer/recordgraph.py
index f69826e9..3fb9e646 100644
--- a/pytrainer/recordgraph.py
+++ b/pytrainer/recordgraph.py
@@ -19,31 +19,15 @@
import logging
from .gui.drawArea import DrawArea
-from gi.repository import Gtk
-
class RecordGraph:
- def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None, btnShowLaps = None, tableConfig = None, pytrainer_main=None):
+ def __init__(self, vbox = None, window = None, pytrainer_main=None):
logging.debug(">>")
self.pytrainer_main = pytrainer_main
self.drawarea = DrawArea(vbox, window)
- self.combovalue = combovalue
- self.combovalue2 = combovalue2
- self.showLaps = btnShowLaps
- self.config_table = tableConfig
logging.debug("<<")
def drawgraph(self,values,laps=None, y1limits=None, y1color=None, y1_linewidth=1):
logging.debug(">>")
- #Get the config options
- for child in self.config_table.get_children():
- if child.get_name() == "spinbuttonY1Max":
- spinbuttonY1Max = child
- elif child.get_name() == "spinbuttonY1Min":
- spinbuttonY1Min = child
- elif child.get_name() == "colorbuttonY1LineColor":
- colorbuttonY1LineColor = child
- elif child.get_name() == "spinbuttonY1LineWeight":
- spinbuttonY1LineWeight = child
xval = []
yval = []
@@ -51,39 +35,17 @@ def drawgraph(self,values,laps=None, y1limits=None, y1color=None, y1_linewidth=1
ylab = []
tit = []
col = []
- value_selected = self.combovalue.get_active()
- logging.debug("Value selected 1: %s", value_selected)
- value_selected2 = self.combovalue2.get_active()
- logging.debug("Value selected 2: %s", value_selected2)
- showLaps = self.showLaps.get_active()
- logging.debug("Show laps: %s", showLaps)
- #Determine left and right lap boundaries
- if laps is not None and showLaps:
- lapValues = []
- lastPoint = 0.0
- for lap in laps:
- thisPoint = float(lap['distance'])/1000.0 + lastPoint
- lapValues.append((lastPoint, thisPoint))
- lastPoint = thisPoint
- else:
- lapValues = None
+ lapValues = None
- if value_selected < 0:
- self.combovalue.set_active(0)
- value_selected = 0
+ value_selected = 0
- if value_selected2 < 0:
- self.combovalue2.set_active(0)
- value_selected2 = 0
+ value_selected2 = 0
xvalues, yvalues = self.get_values(values,value_selected)
max_yvalue = max(yvalues)
min_yvalue = min(yvalues)
xlabel,ylabel,title,color = self.get_value_params(value_selected)
if y1color is not None:
- _color = Gdk.Color(y1color)
color = y1color
- else:
- _color = Gdk.Color(color)
xval.append(xvalues)
yval.append(yvalues)
@@ -95,15 +57,10 @@ def drawgraph(self,values,laps=None, y1limits=None, y1color=None, y1_linewidth=1
tit.append(title)
col.append(color)
- #_color = Gdk.Color(color)
- colorbuttonY1LineColor.set_color(_color)
-
if value_selected2 > 0:
value_selected2 = value_selected2-1
xlabel,ylabel,title,color = self.get_value_params(value_selected2)
xvalues,yvalues = self.get_values(values,value_selected2)
- max_yvalue=max(max(yvalues), max_yvalue)
- min_yvalue=min(min(yvalues), min_yvalue)
xval.append(xvalues)
yval.append(yvalues)
xlab.append(xlabel)
@@ -111,21 +68,7 @@ def drawgraph(self,values,laps=None, y1limits=None, y1color=None, y1_linewidth=1
tit.append("")
col.append(color)
logging.info("To show: tit: %s | col: %s | xlab: %s | ylab: %s", tit, col, xlab, ylab)
- #self.drawPlot(xvalues,yvalues,xlabel,ylabel,title,color,zones)
- plot_stats = self.drawarea.drawPlot(xval,yval,xlab,ylab,tit,col,None,lapValues, ylimits=y1limits, y1_linewidth=y1_linewidth)
- ymin = plot_stats['y1_min']
- ymax = plot_stats['y1_max']
- y1_linewidth = plot_stats['y1_linewidth']
-
- max_yvalue = max(max_yvalue, ymax)
- min_yvalue = min(min_yvalue, ymin)
- adjY1Min = Gtk.Adjustment(value=ymin, lower=min_yvalue,upper=max_yvalue, step_incr=1, page_incr=10)
- adjY1Max = Gtk.Adjustment(value=ymax, lower=min_yvalue,upper=max_yvalue, step_incr=1, page_incr=10)
- spinbuttonY1Min.set_adjustment(adjY1Min)
- spinbuttonY1Max.set_adjustment(adjY1Max)
- spinbuttonY1Min.set_value(ymin)
- spinbuttonY1Max.set_value(ymax)
- spinbuttonY1LineWeight.set_value(y1_linewidth)
+ self.drawarea.drawPlot(xval,yval,xlab,ylab,tit,col,None,lapValues, ylimits=y1limits, y1_linewidth=y1_linewidth)
logging.debug("<<")
diff --git a/pytrainer/test/plugins/test_garmin-tcxv2.py b/pytrainer/test/plugins/test_garmintcxv2.py
similarity index 100%
rename from pytrainer/test/plugins/test_garmin-tcxv2.py
rename to pytrainer/test/plugins/test_garmintcxv2.py