Skip to content

Commit

Permalink
add: create executables using pyinstaller
Browse files Browse the repository at this point in the history
  • Loading branch information
mufasa159 committed Aug 24, 2024
1 parent d37ed39 commit 523465d
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 30 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ __pycache__
*.pyc
src/default/
*.json
*.csv
*.csv
dist/
build/
63 changes: 63 additions & 0 deletions Timer.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# -*- mode: python ; coding: utf-8 -*-


a = Analysis(
['src/main.py'],
pathex=[],
binaries=[],
datas=[
('resources/close.svg', 'resources'),
('resources/dropdown.svg', 'resources'),
('resources/fullscreen.svg', 'resources'),
('resources/icon.icns', 'resources'),
('resources/minimize.svg', 'resources'),
('resources/fonts/satoshi_bold.otf', 'resources/fonts'),
('resources/fonts/satoshi_medium.otf', 'resources/fonts'),
('resources/fonts/satoshi_regular.otf', 'resources/fonts'),
('resources/fonts/license.txt', 'resources/fonts'),
('src/default/settings.json', 'src/default'),
('src/default/timesheet.csv', 'src/default'),
],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='Timer',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['resources/icon.icns'],
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='Timer',
)
app = BUNDLE(
coll,
name='Timer.app',
icon='resources/icon.icns',
bundle_identifier=None,
)
27 changes: 22 additions & 5 deletions readme.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Time Tracker
Timer

The application allows you to track time for different projects.
The data is stored locally in a csv file. The location of the
csv file can be changed from the settings.
A desktop application that allows you to track time for multiple different
projects. The data is stored locally in a csv file. The location of the csv file
can be changed from the settings menu.

---

Expand All @@ -16,7 +16,8 @@ Project Setup
2. Create a virtual environment and activate it

python3 venv .venv
source .venv/bin/activate
source .venv/bin/activate # For Linux and MacOS
.venv\Scripts\activate # For Windows

3. Install the required packages

Expand All @@ -30,3 +31,19 @@ Project Setup

cd src
python3 main.py


---

Creating Executables

1. Install PyInstaller

pip3 install pyinstaller

2. Bundle the application

pyinstaller Timer.spec


It will create a `dist` directory with the executable files.
Binary file added resources/icon.icns
Binary file not shown.
10 changes: 6 additions & 4 deletions src/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

class MainWindow(QMainWindow):
"""
Main application window for the Time Tracker.
Main application window for the Timer.
"""

def __init__(self):
Expand All @@ -46,7 +46,7 @@ def __init__(self):
self.setMaximumHeight(210)
self.setMinimumHeight(210)

self.setWindowTitle("Time Tracker")
self.setWindowTitle("Timer")

# Remove the default title bar
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
Expand All @@ -70,6 +70,10 @@ def __init__(self):
self.project_combo.currentTextChanged.connect(self.project_changed)
self.project_combo.setFont(self.custom_style.regular_font)

initial_project = self.project_combo.currentText()
if initial_project:
self.project_changed(initial_project)

self.settings_button = QPushButton("Settings")
self.view_log_button = QPushButton("Timesheet")

Expand Down Expand Up @@ -110,8 +114,6 @@ def __init__(self):
self.settings_button.clicked.connect(self.open_settings)
self.view_log_button.clicked.connect(self.view_log)

self.refresh_ui()


def toggle_timer(self):
if self.timer.start_time is None:
Expand Down
18 changes: 15 additions & 3 deletions src/project_manager.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import json
import os
import sys


if getattr(sys, 'frozen', False):
script_dir = sys._MEIPASS
settings_dir = os.path.join(script_dir, 'src/default/settings.json')
timesheet_dir = os.path.join(script_dir, 'src/default/timesheet.csv')
else:
script_dir = os.path.dirname(os.path.abspath(__file__))
settings_dir = os.path.join(script_dir, 'default/settings.json')
timesheet_dir = os.path.join(script_dir, 'default/timesheet.csv')


class ProjectManager:
"""
Class to manage projects and settings
"""

def __init__(self, settings_file='default/settings.json'):
def __init__(self, settings_file=settings_dir):
self.settings_file = settings_file
self.settings = self.load_settings()
self.version = self.settings.get('version', '1.0.0')
self.projects = self.settings.get('projects', [])
self.csv_file_path = self.settings.get('csv_file_path', 'default/timesheet.csv')
self.csv_file_path = self.settings.get('csv_file_path', timesheet_dir)


def load_settings(self):
Expand All @@ -21,7 +33,7 @@ def load_settings(self):
return {
"version": "1.0.0",
"projects": [],
"csv_file_path": "default/timesheet.csv"
"csv_file_path": timesheet_dir
}


Expand Down
34 changes: 20 additions & 14 deletions src/style.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from PyQt6.QtGui import QFont, QFontDatabase
import os

import sys

class Style:
"""
Expand All @@ -14,16 +14,22 @@ class Style:
def __init__(self):
super().__init__()

script_dir = os.path.dirname(os.path.abspath(__file__))
font_dir = os.path.join(script_dir, '../resources/fonts')
# Determine if the application is running as a bundled executable
if getattr(sys, 'frozen', False):
script_dir = sys._MEIPASS
font_dir = os.path.join(script_dir, 'resources/fonts')
dropdown_icon = os.path.join(script_dir, 'resources/dropdown.svg')
else:
script_dir = os.path.dirname(os.path.abspath(__file__))
font_dir = os.path.join(script_dir, '../resources/fonts')
dropdown_icon = os.path.join(script_dir, '../resources/dropdown.svg')

self.regular_font_id = QFontDatabase.addApplicationFont(os.path.join(font_dir, "satoshi_regular.otf"))
self.medium_font_id = QFontDatabase.addApplicationFont(os.path.join(font_dir, "satoshi_medium.otf"))
self.bold_font_id = QFontDatabase.addApplicationFont(os.path.join(font_dir, "satoshi_bold.otf"))

if self.regular_font_id == -1:
print("Failed to load font.")
exit()

font_family = QFontDatabase.applicationFontFamilies(self.regular_font_id)[0]
self.regular_font = QFont(font_family)
Expand Down Expand Up @@ -81,31 +87,31 @@ def __init__(self):
text-align: right;
}
"""
self.combo_box = """
QComboBox {
self.combo_box = f"""
QComboBox {{
background-color: #383838;
color: #CACACA;
border: none;
padding: 5px 8px;
border-radius: 4px;
font-size: 14px;
}
QComboBox QAbstractItemView {
}}
QComboBox QAbstractItemView {{
background-color: #383838;
color: #ffffff;
padding: 5px 1px;
border: 1px solid #4F4F4F;
border-radius: 4px;
margin: 0px;
}
QComboBox::drop-down {
}}
QComboBox::drop-down {{
padding-right: 8px;
border: none;
border-radius: 4px;
}
QComboBox::down-arrow {
image: url(../resources/dropdown.svg);
}
}}
QComboBox::down-arrow {{
image: url({dropdown_icon});
}}
"""
self.widget = """
QWidget {
Expand Down
19 changes: 16 additions & 3 deletions src/title_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from PyQt6.QtCore import Qt, QSize, QPoint
from PyQt6.QtGui import QIcon
from style import Style
import sys
import os


class CustomTitleBar(QWidget):
Expand All @@ -10,6 +12,17 @@ def __init__(self, parent=None, allow_fullscreen=True):
self.parent = parent
self.custom_style = Style()

if getattr(sys, 'frozen', False):
script_dir = sys._MEIPASS
close_icon = os.path.join(script_dir, 'resources/close.svg')
minimize_icon = os.path.join(script_dir, 'resources/minimize.svg')
fullscreen_icon = os.path.join(script_dir, 'resources/fullscreen.svg')
else:
script_dir = os.path.dirname(os.path.abspath(__file__))
close_icon = os.path.join(script_dir, '../resources/close.svg')
minimize_icon = os.path.join(script_dir, '../resources/minimize.svg')
fullscreen_icon = os.path.join(script_dir, '../resources/fullscreen.svg')

# Variables to track dragging
self.dragging = False
self.drag_start_position = QPoint()
Expand All @@ -32,14 +45,14 @@ def __init__(self, parent=None, allow_fullscreen=True):

# Add close button to the title bar
close_button = QPushButton()
close_button.setIcon(QIcon("../resources/close.svg"))
close_button.setIcon(QIcon(close_icon))
close_button.setIconSize(QSize(10, 10))
close_button.setStyleSheet(self.custom_style.button_title_bar + "background-color:#BF616A;}")
close_button.setFont(self.custom_style.bold_font)

# Add minimize button to the title bar
minimize_button = QPushButton()
minimize_button.setIcon(QIcon("../resources/minimize.svg"))
minimize_button.setIcon(QIcon(minimize_icon))
minimize_button.setIconSize(QSize(10, 10))
minimize_button.setStyleSheet(self.custom_style.button_title_bar + "background-color:#EBCB8B;}")
minimize_button.setFont(self.custom_style.bold_font)
Expand All @@ -57,7 +70,7 @@ def __init__(self, parent=None, allow_fullscreen=True):
# Add fullscreen button to the title bar
if allow_fullscreen:
fullscreen_button = QPushButton()
fullscreen_button.setIcon(QIcon("../resources/fullscreen.svg"))
fullscreen_button.setIcon(QIcon(fullscreen_icon))
fullscreen_button.setIconSize(QSize(10, 10))
fullscreen_button.setStyleSheet(self.custom_style.button_title_bar + "background-color:#A3BE8C;}")
fullscreen_button.setFont(self.custom_style.bold_font)
Expand Down

0 comments on commit 523465d

Please sign in to comment.