Skip to content

Commit

Permalink
Fixes #18 Added support for application settings
Browse files Browse the repository at this point in the history
  • Loading branch information
TheFriendlyCoder committed Aug 2, 2020
1 parent f0e0352 commit ac66ce9
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 15 deletions.
2 changes: 1 addition & 1 deletion main.pyproject
@@ -1,3 +1,3 @@
{
"files": ["main.py","form.ui","src/friendlypics2/data/ui/main_window.ui","src/friendlypics2/data/ui/form.ui","pinterest_dump.ui","src/friendlypics2/data/ui/pinterest_dump.ui","src/friendlypics2/data/ui/about_dlg.ui"]
"files": ["src/friendlypics2/data/ui/form.ui","src/friendlypics2/data/ui/pinterest_dump.ui","pinterest_dump.ui","src/friendlypics2/data/ui/about_dlg.ui","src/friendlypics2/data/ui/main_window.ui","main.py","form.ui","src/friendlypics2/data/ui/settings_dlg.ui"]
}
2 changes: 2 additions & 0 deletions project.prop
Expand Up @@ -8,6 +8,8 @@
"qtpy",
"pyside2",
"friendlypins",
"pyyaml",
"appdirs"
],
"DEV_DEPENDENCIES" : [
"pytest",
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -37,6 +37,7 @@ pytest==6.0.1
pytest-cov==2.10.0
python-dateutil==2.8.1
pytz==2020.1
PyYAML==5.3.1
QtPy==1.9.0
requests==2.24.0
shiboken2==5.15.0
Expand Down
8 changes: 7 additions & 1 deletion src/friendlypics2/data/ui/about_dlg.ui
Expand Up @@ -2,6 +2,9 @@
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
Expand All @@ -11,7 +14,10 @@
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>About...</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
Expand Down
8 changes: 7 additions & 1 deletion src/friendlypics2/data/ui/main_window.ui
Expand Up @@ -44,6 +44,7 @@
</property>
<addaction name="file_open_menu"/>
<addaction name="separator"/>
<addaction name="file_settings_menu"/>
</widget>
<widget class="QMenu" name="menu_Help">
<property name="title">
Expand Down Expand Up @@ -101,7 +102,12 @@
<bool>true</bool>
</property>
<property name="text">
<string>Debug Window</string>
<string>&amp;Debug Window</string>
</property>
</action>
<action name="file_settings_menu">
<property name="text">
<string>&amp;Settings...</string>
</property>
</action>
</widget>
Expand Down
66 changes: 66 additions & 0 deletions src/friendlypics2/data/ui/settings_dlg.ui
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>settings_dialog</class>
<widget class="QDialog" name="settings_dialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>507</width>
<height>451</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="settings_view"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="cancel_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Cancel</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="save_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Save</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
4 changes: 1 addition & 3 deletions src/friendlypics2/dialogs/about_dlg.py
Expand Up @@ -12,16 +12,14 @@ class AboutDialog(QDialog):
def __init__(self, parent):
super().__init__(parent)
self._log = logging.getLogger(__name__)
self.setWindowTitle("About...")
self.setModal(True)
self.settings = QSettings()
self._load_ui()
# Flag indicating whether the user has requested the GUI settings to be reset.
# If so, the caller should disable any further settings recording logic
self.cleared = False

def _load_ui(self):
"""Internal helper method that configures the UI for the main window"""
"""Internal helper method that configures the UI for the dialog"""
load_ui("about_dlg.ui", self)

self.title_label = self.findChild(QLabel, "title_label")
Expand Down
32 changes: 23 additions & 9 deletions src/friendlypics2/dialogs/main_window.py
Expand Up @@ -7,6 +7,8 @@
from friendlypics2.misc.gui_helpers import load_ui, generate_screen_id, settings_group_context
from friendlypics2.misc.app_helpers import is_mac_app_bundle
from friendlypics2.dialogs.about_dlg import AboutDialog
from friendlypics2.misc.app_settings import AppSettings
from friendlypics2.dialogs.settings_dlg import SettingsDialog


class MainWindow(QMainWindow):
Expand All @@ -17,6 +19,9 @@ def __init__(self):
self._log = logging.getLogger(__name__)
self._disable_window_save = False

# Initialize app settings
self._app_settings = AppSettings()

# Initialize window
self._log.debug("Initializing main window...")
self.setWindowTitle("Friendly Pics")
Expand Down Expand Up @@ -62,9 +67,12 @@ def _load_ui(self):
"""Internal helper method that configures the UI for the main window"""
load_ui("main_window.ui", self)

self.window_debug_menu.triggered.connect(self.window_debug_click)
self.file_open_menu.triggered.connect(self.file_open_click)
self.file_open_menu.setShortcut(QKeySequence.Open)
self.file_settings_menu.triggered.connect(self.file_settings_click)

self.window_debug_menu.triggered.connect(self.window_debug_click)

self.help_about_menu.triggered.connect(self.help_about_click)

# Hack: for testing on MacOS we convert menu bar to non native
Expand All @@ -74,14 +82,6 @@ def _load_ui(self):
if not is_mac_app_bundle():
self.menuBar().setNativeMenuBar(False)

@Slot()
def window_debug_click(self):
"""event handler for when the window->debug menu is clicked"""
if self.window_debug_menu.isChecked():
self.debug_dock.show()
else:
self.debug_dock.hide()

def _load_window_state(self):
"""Restores window layout to it's previous state. Must be called after _load_ui"""
# Load all settings for this specific window
Expand Down Expand Up @@ -136,6 +136,20 @@ def help_about_click(self):
dlg.exec_()
self._disable_window_save = dlg.cleared

@Slot()
def window_debug_click(self):
"""event handler for when the window->debug menu is clicked"""
if self.window_debug_menu.isChecked():
self.debug_dock.show()
else:
self.debug_dock.hide()

@Slot()
def file_settings_click(self):
"""event handler for when the file->settings menu is clicked"""
dlg = SettingsDialog(self, self._settings)
dlg.exec_()

def closeEvent(self, event): # pylint: disable=invalid-name
"""event handler called when the application is about to close
Expand Down
35 changes: 35 additions & 0 deletions src/friendlypics2/dialogs/settings_dlg.py
@@ -0,0 +1,35 @@
"""Logic for the application settings UI"""
import logging
from qtpy.QtWidgets import QDialog
from qtpy.QtCore import Slot, QSettings
from friendlypics2.misc.gui_helpers import load_ui


class SettingsDialog(QDialog):
"""Logic for managing application settings dialog"""
def __init__(self, parent, settings):
super().__init__(parent)
self._settings = settings
self._log = logging.getLogger(__name__)
self.settings = QSettings()
self._load_ui()

def _load_ui(self):
"""Internal helper method that configures the UI for the dialog"""
load_ui("settings_dlg.ui", self)

self.cancel_button.clicked.connect(self.close)
self.save_button.clicked.connect(self._save_clicked)

# TODO: populate the settings tree view
# TODO: make a custom "model" class to render the AppSettings class
self.settings_view.add_item

# Center the about box on the parent window
parent_geom = self.parent().geometry()
self.move(parent_geom.center() - self.rect().center())

@Slot()
def _save_clicked(self):
"""Callback for when the user clicks the save button"""
self._log.debug("Saving")
52 changes: 52 additions & 0 deletions src/friendlypics2/misc/app_settings.py
@@ -0,0 +1,52 @@
"""Interface for persisting application settings"""
import logging
from pathlib import Path
import json

import yaml
from appdirs import user_config_dir
from friendlypics2.version import __version__


class AppSettings:
"""Interface for accessing and persisting application setings"""
def __init__(self):
self._log = logging.getLogger(__name__)
temp = user_config_dir("Friendly Pics 2", "The Friendly Coder", __version__)
self._filename = Path(temp).joinpath("appsettings.yml")
self._log.debug(self._filename)

if self._filename.exists():
self._data = yaml.safe_load(self._filename.read_text())
else:
self._data = dict()
self._data["file_version"] = "1.0"

def __str__(self):
return json.dumps(self._data, indent=4)

@property
def path(self):
"""Location of the config file managed by this class"""
return self._filename

@property
def pinterest_user(self):
"""str: user to authenticate with to Pinterest"""
return self._data.get("services", dict()).get("pinterest", dict()).get("username")

@pinterest_user.setter
def pinterest_user(self, value):
# TODO: consider saving config data every time a setter is accessed
self._data["services"]["pinterest"]["username"] = value

@property
def file_version(self):
"""str: gets the schema version for the config file"""
assert "file_version" in self._data
return self._data.get("file_version")

def save(self):
"""Saves the current contents of the app settings for later reference"""
with self._filename.open("w") as config_file:
yaml.safe_dump(self._data, config_file)

0 comments on commit ac66ce9

Please sign in to comment.