From ca475dc0baf6a4f553c8e5ec0ac09e0e734f15a2 Mon Sep 17 00:00:00 2001 From: Colin Pitrat Date: Mon, 24 Jun 2024 08:55:32 +0100 Subject: [PATCH 01/22] Update installation from source and fix broken link. #268 --- README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f4ee3f8..37efedb0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,21 @@ Source Repository Structure * **schemas** schemas to support correct xml parsing * **utils** localization shell script -Installation from source tarball +Installation from source tarball (using pip) +----------------- +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` + +`$ pip install pycairo pygobject` + +`$ pip install .` + +`$ pytrainer -i` + +Installation from source tarball (deprecated method) ----------------- 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`. @@ -29,7 +43,7 @@ Copy tarball file to a location where you have write and execution rights (e.g. `$ pytrainer -i` -For more information about the process, please check [Distutils documentation] (http://docs.python.org/distutils/setupscript.html) +For more information about the process, please check [Distutils documentation] (http://docs.python.org/3.11/distutils/setupscript.html) Further Resources ----------------- From c374fdc6004348552e2c5dc081f048bd08c1e646 Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Tue, 13 Feb 2024 11:56:16 +0200 Subject: [PATCH 02/22] Rename the plugin test to make it discoverable by unittests Earlier this would be run by `python3 setup.py test` but not by `python3 -m unittest`, which was pretty wonky. --- .../test/plugins/{test_garmin-tcxv2.py => test_garmintcxv2.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pytrainer/test/plugins/{test_garmin-tcxv2.py => test_garmintcxv2.py} (100%) 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 From 8b5a3a4f336fb4b68d7686bd9ff280b705b984b5 Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Tue, 13 Feb 2024 11:57:13 +0200 Subject: [PATCH 03/22] Replace "setup.py test" with "unittest" The former is deprecated and will be removed. --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From fe13c83b61809e5501f77dd2d055f8fe85c0434a Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Tue, 13 Feb 2024 12:00:52 +0200 Subject: [PATCH 04/22] Reindent plugins.py --- pytrainer/plugins.py | 210 +++++++++++++++++++++---------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/pytrainer/plugins.py b/pytrainer/plugins.py index b42c5ace..10076bb2 100644 --- a/pytrainer/plugins.py +++ b/pytrainer/plugins.py @@ -25,108 +25,108 @@ 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() + 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) From 2023bec0c864cb4b40d484136590807ff9ac1541 Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Thu, 27 Jun 2024 08:35:38 +0200 Subject: [PATCH 05/22] Reindent garmin-tcxv2.py --- plugins/garmin-tcxv2/garmin-tcxv2.py | 182 +++++++++++++-------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/plugins/garmin-tcxv2/garmin-tcxv2.py b/plugins/garmin-tcxv2/garmin-tcxv2.py index e50bae4b..182a7260 100644 --- a/plugins/garmin-tcxv2/garmin-tcxv2.py +++ b/plugins/garmin-tcxv2/garmin-tcxv2.py @@ -26,102 +26,102 @@ 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.... + 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') From 764ec44c1f1cdbd3bd530289ec25bda5c07614f8 Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Tue, 13 Feb 2024 12:02:50 +0200 Subject: [PATCH 06/22] Move imports around to make plugins testable without the GUI --- plugins/garmin-tcxv2/garmin-tcxv2.py | 2 +- pytrainer/plugins.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/garmin-tcxv2/garmin-tcxv2.py b/plugins/garmin-tcxv2/garmin-tcxv2.py index 182a7260..af5e8bf8 100644 --- a/plugins/garmin-tcxv2/garmin-tcxv2.py +++ b/plugins/garmin-tcxv2/garmin-tcxv2.py @@ -21,7 +21,6 @@ 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 @@ -48,6 +47,7 @@ def getConfValue(self, confVar): 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 = [] diff --git a/pytrainer/plugins.py b/pytrainer/plugins.py index 10076bb2..5dfdc2f4 100644 --- a/pytrainer/plugins.py +++ b/pytrainer/plugins.py @@ -22,7 +22,6 @@ from .lib.xmlUtils import XMLParser -from .gui.windowplugins import WindowPlugins class Plugins: def __init__(self, data_path = None, parent = None): @@ -68,6 +67,7 @@ def importClass(self, pathPlugin): def managePlugins(self): pluginsList = self.getPluginsList() + from .gui.windowplugins import WindowPlugins windowplugins = WindowPlugins(self.data_path, self) windowplugins.setList(pluginsList) windowplugins.run() From 35199001e6cfa4be1a4c5d77b0a74d4f5527b858 Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Sat, 29 Jun 2024 07:58:11 +0200 Subject: [PATCH 07/22] Add "gui" to the list of optional dependencies This may be useful when installing from source, and is definitely useful when installing into a venv for development. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) 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"] From 10f80581a4623f2b3674454b5b8fec497a181c70 Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Sat, 29 Jun 2024 09:39:45 +0200 Subject: [PATCH 08/22] Drop the source repository structure section from the readme --- README.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/README.md b/README.md index 37efedb0..50235fd1 100644 --- a/README.md +++ b/README.md @@ -5,18 +5,6 @@ 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 (using pip) ----------------- 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`. From ae007fb0d12a0fa423c1b6543dc74c780929f391 Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Sat, 29 Jun 2024 09:41:09 +0200 Subject: [PATCH 09/22] Rewrite the installation instructions in the readme --- README.md | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 50235fd1..e73d1a0f 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,28 @@ 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. +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 from source tarball (using pip) ------------------ -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` +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. -`$ pip install pycairo pygobject` - -`$ pip install .` - -`$ pytrainer -i` - -Installation from source tarball (deprecated method) +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`. +Copy tarball file to a location where you have write and execution rights (e.g. your `$HOME` directory). `$ tar -xzf pytrainer-X.Y.Z.tar.gz` `$ cd pytrainer-X.Y.Z` -`$ sudo python setup.py install` +`$ pip install ."[gui]"` `$ pytrainer -i` -For more information about the process, please check [Distutils documentation] (http://docs.python.org/3.11/distutils/setupscript.html) - Further Resources ----------------- * FAQ [https://github.com/pytrainer/pytrainer/wiki/FAQ](https://github.com/pytrainer/pytrainer/wiki/FAQ) From 449d39566ee1edb042f9cbca66d594dca2ba375d Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Sat, 29 Jun 2024 09:43:06 +0200 Subject: [PATCH 10/22] Add section about installing a dev environment to the readme --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index e73d1a0f..61c68e27 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,23 @@ Copy tarball file to a location where you have write and execution rights (e.g. `$ pytrainer -i` +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. + +`git clone https://github.com/pytrainer/pytrainer.git` + +`cd pytrainer` + +`python3 -m venv .venv` + +`.venv/bin/pip install -e ".[gui]"` + +`.venv/bin/pytrainer -i` + Further Resources ----------------- * FAQ [https://github.com/pytrainer/pytrainer/wiki/FAQ](https://github.com/pytrainer/pytrainer/wiki/FAQ) From 300bf7c092be3e829313d30e85f0de87fc1f2ad8 Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Sat, 29 Jun 2024 09:45:29 +0200 Subject: [PATCH 11/22] Include the remaining information from INSTALL in the readme, and remove it --- INSTALL | 43 ------------------------------------------- README.md | 12 ++++++++++++ pytrainer/platform.py | 2 +- 3 files changed, 13 insertions(+), 44 deletions(-) delete mode 100644 INSTALL 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 61c68e27..775cf1c5 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,18 @@ environment. `.venv/bin/pytrainer -i` +Additional packages +------------------- +For map functionality the GIR bindings for WebKit2 need to be installed. + +* 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) + Further Resources ----------------- * FAQ [https://github.com/pytrainer/pytrainer/wiki/FAQ](https://github.com/pytrainer/pytrainer/wiki/FAQ) 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/") From 5949d13917a5166e5831c9687225b92da36b774e Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Sat, 29 Jun 2024 12:40:56 +0200 Subject: [PATCH 12/22] Add missing "label" keyword to CheckMenuItem initialization --- pytrainer/gui/windowmain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytrainer/gui/windowmain.py b/pytrainer/gui/windowmain.py index 938e4952..4d4854be 100644 --- a/pytrainer/gui/windowmain.py +++ b/pytrainer/gui/windowmain.py @@ -1389,7 +1389,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) From 971cdea5bd6c0563b5aa9d915426854b400039ee Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Sat, 29 Jun 2024 12:42:14 +0200 Subject: [PATCH 13/22] Add missing keywords to ImageMenuItem initialization --- pytrainer/gui/popupmenu.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) 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 From f739408242aff985754bd04c313d14aa756eae39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Jerning?= Date: Fri, 5 Jul 2024 17:52:32 +0200 Subject: [PATCH 14/22] Use code blocks in the readme --- README.md | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 775cf1c5..1332bf39 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,12 @@ Installation from source tarball ----------------- Copy tarball file to a location where you have write and execution rights (e.g. your `$HOME` directory). -`$ tar -xzf pytrainer-X.Y.Z.tar.gz` - -`$ cd pytrainer-X.Y.Z` - -`$ pip install ."[gui]"` - -`$ pytrainer -i` +```shell +$ tar -xzf pytrainer-X.Y.Z.tar.gz +$ cd pytrainer-X.Y.Z +$ pip install ."[gui]" +$ pytrainer -i +``` Installation into a venv (for development) ------------------------------------------ @@ -30,15 +29,13 @@ 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. -`git clone https://github.com/pytrainer/pytrainer.git` - -`cd pytrainer` - -`python3 -m venv .venv` - -`.venv/bin/pip install -e ".[gui]"` - -`.venv/bin/pytrainer -i` +```shell +git clone https://github.com/pytrainer/pytrainer.git +cd pytrainer +python3 -m venv .venv +.venv/bin/pip install -e ".[gui]" +.venv/bin/pytrainer -i +``` Additional packages ------------------- From 8242716709d6a9fe7b0bd964fd54922a96080f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Jerning?= Date: Fri, 5 Jul 2024 17:53:40 +0200 Subject: [PATCH 15/22] Use three header levels in the readme --- README.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1332bf39..e182c79d 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,15 @@ -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. -Installation -============ +## 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 ------------------ +### Installation from source tarball Copy tarball file to a location where you have write and execution rights (e.g. your `$HOME` directory). ```shell @@ -22,8 +19,7 @@ $ pip install ."[gui]" $ pytrainer -i ``` -Installation into a venv (for development) ------------------------------------------- +### 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 @@ -37,8 +33,7 @@ python3 -m venv .venv .venv/bin/pytrainer -i ``` -Additional packages -------------------- +### Additional packages For map functionality the GIR bindings for WebKit2 need to be installed. * gpsbabel == 1.3.5 ("GoogleEarth" and "Garmin via GPSBabel 1.3.5" aka "garmin_hr") @@ -49,8 +44,7 @@ For map functionality the GIR bindings for WebKit2 need to be installed. * GDAL (Elevation correction, via "gdal-python" or "python-gdal") * perl (garmin-fit plugin) -Further Resources ------------------ +## 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) From faff000bf1d3e0645ade9491aba5947f2bf43010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Jerning?= Date: Fri, 5 Jul 2024 17:54:03 +0200 Subject: [PATCH 16/22] Remove prompt indicators in the readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e182c79d..d6a13fe5 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ available for your system you can install from source, see below. 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 -i +tar -xzf pytrainer-X.Y.Z.tar.gz +cd pytrainer-X.Y.Z +pip install ."[gui]" +pytrainer -i ``` ### Installation into a venv (for development) From 6afc7304389784b495375b81b60409c00545c1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Jerning?= Date: Fri, 5 Jul 2024 17:55:08 +0200 Subject: [PATCH 17/22] Omit -i option in the readme Omit the option to start pytrainer with info level logging in the install instructions so it's not misinterpreted as an installation step. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6a13fe5..e7432244 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Copy tarball file to a location where you have write and execution rights (e.g. tar -xzf pytrainer-X.Y.Z.tar.gz cd pytrainer-X.Y.Z pip install ."[gui]" -pytrainer -i +pytrainer ``` ### Installation into a venv (for development) @@ -30,7 +30,7 @@ git clone https://github.com/pytrainer/pytrainer.git cd pytrainer python3 -m venv .venv .venv/bin/pip install -e ".[gui]" -.venv/bin/pytrainer -i +.venv/bin/pytrainer ``` ### Additional packages From 472c71e229e66cd4eb34b1140a8b0b5158e7ad9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Jerning?= Date: Sat, 6 Jul 2024 19:21:51 +0200 Subject: [PATCH 18/22] Create a table in the readme for packages needed by plugins --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e7432244..316f5740 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,17 @@ python3 -m venv .venv ### Additional packages For map functionality the GIR bindings for WebKit2 need to be installed. -* 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) +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) From a3bd1db22d0807c029414da066a4eb25e694832d Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Sat, 7 Sep 2024 07:00:11 +0200 Subject: [PATCH 19/22] Enable passing version info to setuptools_scm via git-archive See the "Git archives" section at https://setuptools-scm.readthedocs.io/en/latest/usage/ for the documentation this is based on. --- .git_archival.txt | 3 +++ .gitattributes | 1 + 2 files changed, 4 insertions(+) create mode 100644 .git_archival.txt create mode 100644 .gitattributes 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 From eda968a8b48074f03efbdfbd692b46edef3658cd Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Wed, 8 Oct 2025 22:39:39 +0200 Subject: [PATCH 20/22] MapViewer: Upgrade to WebKitGTK 4.1 ABI WebKitGTK will be dropping support for the unmaintained libsoup 2 (ABI 4.0): https://webkitgtk.org/2025/10/07/webkitgtk-soup2-deprecation.html Since we do not depend on libsoup 2 through any other dependency, there is no risk of conflicts so this should be entirely safe (no API changes). --- pytrainer/extensions/mapviewer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 93052370115a9e648be9e4d6b96f474257469d9d Mon Sep 17 00:00:00 2001 From: Arto Jantunen Date: Tue, 13 Feb 2024 19:30:34 +0200 Subject: [PATCH 21/22] Remove the "old" graph mode This hasn't worked in years, so no point in carrying the option and code. --- glade/profile.ui | 45 ----- man/pytrainer.1 | 4 - pytrainer/__main__.py | 13 -- pytrainer/gui/windowmain.py | 320 ++++++++++++++++----------------- pytrainer/gui/windowprofile.py | 18 +- 5 files changed, 155 insertions(+), 245 deletions(-) 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/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/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/gui/windowmain.py b/pytrainer/gui/windowmain.py index 4d4854be..9c5ce2b0 100644 --- a/pytrainer/gui/windowmain.py +++ b/pytrainer/gui/windowmain.py @@ -550,175 +550,161 @@ 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) + #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 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) 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() From f2e8f952c5beb9fbcbdbd2a76911d24cd592bba9 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sun, 19 Oct 2025 02:51:56 +0200 Subject: [PATCH 22/22] Remove old graph mode config --- glade/pytrainer.ui | 787 +----------------------------------- pytrainer/gui/windowmain.py | 55 +-- pytrainer/recordgraph.py | 67 +-- 3 files changed, 8 insertions(+), 901 deletions(-) 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/pytrainer/gui/windowmain.py b/pytrainer/gui/windowmain.py index 9c5ce2b0..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,8 +550,6 @@ def actualize_recordgraph(self,activity): self.record_list = activity.tracks self.laps = activity.laps if activity.gpx_file is not None: - #Hide current drop down boxes - self.hbox30.hide() self.graph_data_hbox.hide() #Enable graph self.record_vbox.set_sensitive(1) @@ -745,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() @@ -1572,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() @@ -1618,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) @@ -1728,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/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("<<")