Skip to content

Commit

Permalink
The first release candidate
Browse files Browse the repository at this point in the history
  • Loading branch information
t0mmili committed Jul 26, 2024
1 parent a6d223d commit a55584f
Show file tree
Hide file tree
Showing 10 changed files with 423 additions and 2 deletions.
183 changes: 183 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml

# ruff
.ruff_cache/

# LSP config files
pyrightconfig.json

# End of https://www.toptal.com/developers/gitignore/api/python

# Application custom

# Schemes used for development
schemes/

# End of Application custom
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,24 @@
# dc-themer
Python app aiming to speed up switching between Double Commander color schemes
# DC Themer
A free open source application aiming to complement the functionality of Double Commander with a theme switcher.

## Key features
* Switch themes instantly with just one click.
* Backup configuration before applying new theme.
* For other planned cool features check [TODO.md](TODO.md).

## Stack
DC Themer is written in [Python 3](https://www.python.org/), with the [Tkinter](https://wiki.python.org/moin/TkInter) package for the GUI.

## Prerequisites
There are no special requirements to my knowledge.

For the time being DC Themer is released as Windows binary, but feel free to pull the source code and run/compile on other machines.

## Quick start
1. Download [DC Themer latest version](https://github.com/t0mmili/dc-themer/releases/latest).
2. Download [themes]((https://github.com/t0mmili/dc-themes)) from my other GitHub repo.
3. Put **schemes** folder next to **dc-themer.exe**.
> **NOTE**
> Keep the folder name as is.
4. Run **dc-themer.exe**.
5. Profit!
12 changes: 12 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DC Themer to-do

| Item | Details | Priority |
|---|---|---|
| Documentation - user || <span style="color:orange">Medium</span> |
| Documentation - code | Investigate Python documentation standard and implement. | <span style="color:orange">Medium</span> |
| User config | - Implement config in json format.<br>- Create default, if doesn't exist, on app start.<br>- In-app window to modify.<br>- Versioning, in case new config values appear in the future. | <span style="color:orange">Medium</span> |
| Detailed message after completing scheme apply | Should mention what was done, e.g. DC config backup. | <span style="color:yellow">Low</span> |
| DC config compatibility | - Check scheme version against config version.<br>- Display warning if version mismatch. | <span style="color:yellow">Low</span> |
| Scheme export | Allow to export scheme from current DC config. | <span style="color:yellow">Low</span> |
| Dark Mode | Implement DC's Dark Mode handling. | <span style="color:yellow">Low</span> |
| License link | Link in About window. | <span style="color:yellow">Low</span> |
17 changes: 17 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# General application information
APP_AUTHOR = 't0mmili'
APP_NAME = 'DC Themer'
APP_VERSION = '0.1.0'
DEV_YEARS = '2024'
REPO_URL = 'https://github.com/t0mmili/dc-themer'

# Application assets
ICON_PATH = './assets/dct-icon-v3.ico'

# Application config
DC_CONFIG_PATH = '%APPDATA%\\doublecmd'
SCHEME_EXTENSION = 'xml'
SCHEME_PATH = './schemes'
SCHEME_TAGS = ['Colors','Fonts']
WINDOW_HEIGHT = 115
WINDOW_WIDTH = 285
66 changes: 66 additions & 0 deletions app/gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import tkinter as tk
import webbrowser
from config import APP_AUTHOR, APP_NAME, APP_VERSION, DC_CONFIG_PATH, DEV_YEARS, REPO_URL, SCHEME_EXTENSION, SCHEME_PATH, SCHEME_TAGS
from tkinter import ttk
from tkinter.messagebox import showerror, showinfo
from utils import Scheme

class AppMenuBar:
def __init__(self, parent):
about_message = (f'{APP_NAME} v{APP_VERSION}\n\n'
f'Copyright (c) {DEV_YEARS} {APP_AUTHOR}. All rights reserved.\n\n'
f'This is open source software, released under the MIT License.')

# Initialize Menu Bar
self.menu_bar = tk.Menu(parent)

# Add Menu Bar items
self.file_menu = tk.Menu(self.menu_bar, tearoff=False)
self.file_menu.add_command(label='Exit', command=lambda: parent.quit())
self.menu_bar.add_cascade(label='File', menu=self.file_menu)

self.help_menu = tk.Menu(self.menu_bar, tearoff=False)
self.help_menu.add_command(label=f'{APP_NAME} on GitHub', command=lambda: webbrowser.open(REPO_URL))
self.help_menu.add_command(label='About', command=lambda: showinfo(title='About', message=about_message))
self.menu_bar.add_cascade(label='Help', menu=self.help_menu)

class AppFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)

# Field options
options = {'padx': 5, 'pady': 5}

# Scheme Selector label
self.scheme_selector_label = ttk.Label(self, text='Select scheme:')
self.scheme_selector_label.grid(column=0, row=0, sticky=tk.W, **options)

# Scheme Selector
scheme_path = SCHEME_PATH
scheme_ext = SCHEME_EXTENSION
schemes = Scheme.list_schemes(scheme_path, scheme_ext)
self.scheme_var = tk.StringVar(self)

self.scheme_selector = ttk.OptionMenu(self, self.scheme_var, schemes[0], *schemes)
self.scheme_selector.grid(column=1, row=0, **options)

# Apply Scheme button
self.apply_button = ttk.Button(self, text='Apply')
self.apply_button['command'] = self.modify_scheme
self.apply_button.grid(column=0, row=1, columnspan=2, sticky=tk.W, **options)

# Add Frame options
self.grid(padx=10, pady=10, sticky=tk.NSEW)

def modify_scheme(self):
try:
dc_config = DC_CONFIG_PATH
scheme = self.scheme_var.get()
scheme_path = SCHEME_PATH
tags = SCHEME_TAGS
Scheme.apply_scheme(scheme, scheme_path, dc_config, tags)
showinfo(title='Info', message=f'Scheme \'{scheme}\' applied successfully.')
except FileNotFoundError as e:
showerror(title='Error', message=str(e))
except Exception as e:
showerror(title='Error', message=f'An error occurred:\n{str(e)}')
38 changes: 38 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import tkinter as tk
from config import APP_NAME, ICON_PATH, WINDOW_HEIGHT, WINDOW_WIDTH
from gui import AppFrame, AppMenuBar
from os import path

class App(tk.Tk):
def __init__(self):
super().__init__()

# Main Window size
window_width = WINDOW_WIDTH
window_height = WINDOW_HEIGHT

icon_path = ICON_PATH
# This is needed for compilation with PyInstaller
# icon_path = path.abspath(path.join(path.dirname(__file__), ICON_PATH))

self.iconbitmap(icon_path)
self.resizable(False, False)
self.title(APP_NAME)

# Create an instance of Menu Bar
self.menu = AppMenuBar(self)
self.config(menu=self.menu.menu_bar)

# Find screen center point
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
center_x = int(screen_width/2 - window_width/2)
center_y = int(screen_height/2 - window_height/2)

# Set Main Window geometry
self.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}')

if __name__ == '__main__':
app = App()
AppFrame(app)
app.mainloop()
1 change: 1 addition & 0 deletions app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
defusedxml==0.7.1
Loading

0 comments on commit a55584f

Please sign in to comment.