diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml index 6052ed5..dae00b6 100644 --- a/AppImageBuilder.yml +++ b/AppImageBuilder.yml @@ -42,22 +42,30 @@ AppDir: - python3.9-minimal - python3-numpy - python3-scipy - # - python3-matplotlib - # - python-matplotlib-data - python3-bsddb3 - python3-h5py - python3-pyqt5 - python3-pyqtgraph - - python3-requests - - python3-urllib3 -# - python3-tk exclude: + # lots of qt stuff we do not use + - libqt5designer5 + - libqt5help5 + - libqt5network5 + - libqt5sql5 + - libqt5test5 + - libqt5xml5 + - qtbase5-dev-tools + - qtchooser + - pyqt5-dev-tools + - qtchooser - libavahi-client3 - libavahi-common-data - libavahi-common3 + - libwacom2 + - libwacom-common after_bundle: | echo "MONSTER SED FOLLOWING...(uncomment if needed for mpl-data)" - #sed -i s,\'/usr/share/matplotlib/mpl-data\',"f\"\{os.environ.get\('APPDIR'\,'/'\)\}/usr/share/matplotlib/mpl-data\"", ${TARGET_APPDIR}/usr/lib/python3/dist-packages/matplotlib/__init__.py + # sed -i s,\'/usr/share/matplotlib/mpl-data\',"f\"\{os.environ.get\('APPDIR'\,'/'\)\}/usr/share/matplotlib/mpl-data\"", ${TARGET_APPDIR}/usr/lib/python3/dist-packages/matplotlib/__init__.py runtime: version: "continuous" env: diff --git a/src/gui_qt/_py/basewindow.py b/src/gui_qt/_py/basewindow.py index f680529..07a2cc1 100644 --- a/src/gui_qt/_py/basewindow.py +++ b/src/gui_qt/_py/basewindow.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'src/resources/_ui/basewindow.ui' # -# Created by: PyQt5 UI code generator 5.15.7 +# Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -358,6 +358,8 @@ class Ui_BaseWindow(object): self.actionBugs.setObjectName("actionBugs") self.actionShow_error_log = QtWidgets.QAction(BaseWindow) self.actionShow_error_log.setObjectName("actionShow_error_log") + self.actionCreate_starter = QtWidgets.QAction(BaseWindow) + self.actionCreate_starter.setObjectName("actionCreate_starter") self.menuSave.addAction(self.actionSave) self.menuSave.addAction(self.actionExportGraphic) self.menuSave.addAction(self.action_save_fit_parameter) @@ -422,6 +424,7 @@ class Ui_BaseWindow(object): self.menuOptions.addSeparator() self.menuOptions.addAction(self.action_colorcycle) self.menuOptions.addAction(self.actionConfiguration) + self.menuOptions.addAction(self.actionCreate_starter) self.menuView.addAction(self.actionTile) self.menuView.addAction(self.actionCascade_windows) self.menuWindow.addAction(self.actionNew_window) @@ -612,6 +615,7 @@ class Ui_BaseWindow(object): self.action_draw_object.setText(_translate("BaseWindow", "Draw objects...")) self.actionBugs.setText(_translate("BaseWindow", "Bugs! Problems! Wishes!")) self.actionShow_error_log.setText(_translate("BaseWindow", "Show error log")) + self.actionCreate_starter.setText(_translate("BaseWindow", "Create starter..")) from ..data.datawidget.datawidget import DataWidget from ..data.integral_widget import IntegralWidget from ..data.point_select import PointSelectWidget diff --git a/src/gui_qt/lib/starter.py b/src/gui_qt/lib/starter.py new file mode 100644 index 0000000..748c379 --- /dev/null +++ b/src/gui_qt/lib/starter.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import re +from shutil import copyfile +from pathlib import Path +import os +from configparser import ConfigParser +from importlib.resources import path as resource_path + +from nmreval.configs import config_paths +from nmreval.lib.logger import logger + + +def make_starter(app_file: str | None): + if app_file is not None: + make_starter_appimage(Path(app_file)) + else: + make_starter_src() + + +def make_starter_appimage(app_file: Path): + new_path = Path.home() / '.local' / 'bin' / app_file.name + + if app_file != new_path: + app_file.rename(new_path) + + create_desktop_file(new_path) + + +def make_starter_src(): + home = Path.home() + p = Path.home() + for p in Path(__file__).parents: + if p.stem == 'src': + break + elif p == home: + break + + success = p != Path.home() + if success: + bin_path = p.with_name('bin') / 'evaluate.py' + success = bin_path.exists() + + if not success: + logger.warning('Location of evaluate.py could not be determined') + return False + + create_desktop_file(bin_path) + + +def create_desktop_file(new_path: Path): + logo_path = config_paths() / 'logo.png' + if not logo_path.exists(): + with resource_path('resources', 'logo.png') as fp: + copyfile(fp, logo_path) + desktop_entry = f"""\ +[Desktop Entry] +Name=NMReval +Comment=Best program ever (maybe) +Exec={new_path} +Icon={logo_path} +Type=Application +Terminal=false +Categories=Science;NumericalAnalysis;Physics;DataVisualization;Other; +NoDisplay=false +""" + file_name = 'pkm.vogel.nmreval.desktop' + with Path('~/.local/share/applications/', file_name).expanduser().open('w') as f: + f.write(desktop_entry) + + desktop_dir = get_xkg_user_dirs('desktop') + if desktop_dir is not None: + desk_file = Path(desktop_dir, file_name) + with desk_file.open('w') as f: + f.write(desktop_entry) + + desk_file.chmod(0o755) + + +def get_xkg_user_dirs(dir_type: str) -> str | None: + xdg_conf_home = os.getenv('XDG_CONFIG_HOME') or str(Path.home() / '.config') + + with Path(xdg_conf_home, 'user-dirs.dirs').open('r') as f: + conf_string = '[XDG_USER_DIRS]\n' + f.read() + conf_string = re.sub(r'\$HOME', str(Path.home()), conf_string) + conf_string = re.sub('"', '', conf_string) + + config = ConfigParser() + config.read_string(conf_string) + + return config['XDG_USER_DIRS'].get(f'xdg_{dir_type}_dir') + + +if __name__ == '__main__': + make_starter() diff --git a/src/gui_qt/lib/utils.py b/src/gui_qt/lib/utils.py index 436e656..d2c1680 100644 --- a/src/gui_qt/lib/utils.py +++ b/src/gui_qt/lib/utils.py @@ -1,6 +1,7 @@ from __future__ import annotations -from functools import lru_cache +import os +import urllib.request from os import getenv, stat from os.path import exists import hashlib @@ -8,8 +9,7 @@ import subprocess from datetime import datetime from contextlib import contextmanager from pathlib import Path - -import requests +from urllib.error import HTTPError from numpy import linspace from scipy.interpolate import interp1d @@ -64,7 +64,7 @@ class RdBuCMap: class UpdateDialog(QtWidgets.QDialog): - startDownload = QtCore.pyqtSignal(list) + startDownload = QtCore.pyqtSignal(tuple) def __init__(self, filename: str = None, parent=None): super().__init__(parent=parent) @@ -74,7 +74,6 @@ class UpdateDialog(QtWidgets.QDialog): filename = getenv('APPIMAGE') self._appfile = filename - self.success = False self.updater = Updater() self.thread = QtCore.QThread(self) @@ -144,24 +143,20 @@ class UpdateDialog(QtWidgets.QDialog): @QtCore.pyqtSlot() def update_appimage(self): if self._appfile is None: - args = [self.updater.zsync_url] + args = (self.updater.zsync_url,) else: # this breaks the download for some reason - args = ['-i', self._appfile, self.updater.zsync_url] - - args = [self.updater.zsync_url] + args = (self.updater.zsync_url, self._appfile) self.dialog_button.setEnabled(False) self.startDownload.emit(args) self.status.show() - @QtCore.pyqtSlot(int) - def finish_update(self, retcode: int): - # print('finished with', retcode) - self.success = retcode == 0 + @QtCore.pyqtSlot(int, str) + def finish_update(self, retcode: int, file_loc: str): if retcode == 0: - self.status.setText('Download complete.') + self.status.setText(f'Download complete.New AppImage lies in
{file_loc}.
') else: self.status.setText(f'Download failed :( with return code {retcode}.') self.dialog_button.setStandardButtons(QtWidgets.QDialogButtonBox.Close) @@ -171,44 +166,49 @@ class UpdateDialog(QtWidgets.QDialog): self.thread.quit() self.thread.wait() - if self.success: - appname = self.updater.get_zsync()[2] - if self._appfile is not None: - appimage_path = appname - old_version = Path(self._appfile).rename(self._appfile+'.old') - appimage_path = Path(appimage_path).replace(self._appfile) - else: - appimage_path = Path().cwd() / appname - # rename to version-agnostic name - appimage_path = appimage_path.rename('NMReval.AppImage') - appimage_path.chmod(appimage_path.stat().st_mode | 73) # 73 = 0o111 = a+x - - _ = QtWidgets.QMessageBox.information(self, 'Complete', - f'New AppImage available at