Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Code, Update .gitignore, Add Fallback Installation Command #29

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
14 changes: 8 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/build
/dist
/blinkEye.exe
/blinkEye.spec
/BlinkEye.spec
/ExecutableFile
build/
dist/
BlinkEye.spec
blinkEye.exe
blinkEye.spec
BlinkEye.spec
ExecutableFile
.venv
__pycache__/
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Here's how to get started with Blink Eye:
```
4. **Optional: Build .exe file**:
```bash
pyinstaller --name BlinkEye --onefile --windowed --icon=blink-eye-logo.ico --hidden-import plyer.platforms.win.notification blink-eye.py
pyinstaller --name BlinkEye --onefile --windowed --icon=blink-eye-logo.ico --hidden-import plyer.platforms.win.notification blink_eye.py
```
This will create an executable file for Blink Eye.

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ The **20-20-20 rule** is a guideline to reduce eye strain caused by staring at s

#### Windows

> Run the commands in the Command Prompt (CMD). Not in PowerShell.

1. Clone the repository:

```command
Expand Down Expand Up @@ -89,7 +91,7 @@ The **20-20-20 rule** is a guideline to reduce eye strain caused by staring at s
4. If you want to make ```.exe``` by yourself

```bash
pyinstaller --name BlinkEye --onefile --windowed --icon=blink-eye-logo.ico --hidden-import plyer.platforms.win.notification blink-eye.py
pyinstaller --name BlinkEye --onefile --windowed --icon=blink-eye-logo.ico --hidden-import plyer.platforms.win.notification blink_eye.py


## Usage
Expand Down
135 changes: 89 additions & 46 deletions application/blink-eye.py → application/blink_eye.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import tkinter as tk
import threading
import os
import sys
import time
import pystray
import threading
import webbrowser
from tkinter import PhotoImage
from plyer import notification
import webbrowser
import sys
import os
from datetime import datetime
import pystray
from pystray import MenuItem as item
from PIL import Image
from typing import List


def resource_path(relative_path):
try:
Expand All @@ -22,7 +24,12 @@ def resource_path(relative_path):
base_path = os.path.abspath("./application/Assets")
return os.path.join(base_path, relative_path)


class BlinkEyeApp:
POPUP_INTERVAL = 1200 # 20 minutes
FADE_INTERVAL = 0.1 # 100 milliseconds
FADE_VALUES = [i / 10 for i in range(11)]

def __init__(self):
self.root = tk.Tk()
self.launched_time = 0
Expand All @@ -31,75 +38,102 @@ def __init__(self):
def setup_window(self):
self.root.title("Blink Eye")
self.root.attributes("-fullscreen", True)
self.root.configure(bg='black')
self.root.attributes('-alpha', 0.0)
self.root.configure(bg="black")
self.root.attributes("-alpha", 0.0)
self.load_images()
self.create_widgets()

def load_images(self):
self.logo_image = tk.PhotoImage(file=resource_path("blink-eye-logo.png"))
self.button_image = tk.PhotoImage(file=resource_path("blink-eye-reminder-btn.png"))
self.button_image = tk.PhotoImage(
file=resource_path("blink-eye-reminder-btn.png")
)

def create_widgets(self):
self.counter_label = tk.Label(self.root, text="", font=("Helvetica", 160), fg='white', bg='black')
self.counter_label.place(relx=0.5, rely=0.4, anchor='center')

self.time_label = tk.Label(self.root, text="", font=("Helvetica", 24), fg='white', bg='black')
self.time_label.place(relx=0.5, rely=0.7, anchor='center')

self.quote_label = tk.Label(self.root, text="Look 20 feet far away to protect your eyes", font=("Helvetica", 32), fg='white', bg='black')
self.quote_label.place(relx=0.5, rely=0.8, anchor='center')

self.skip_button = tk.Button(self.root, image=self.button_image, command=self.skip_reminder, cursor='hand2', borderwidth=0, highlightthickness=0, relief=tk.FLAT, activebackground='black', activeforeground='black')
self.skip_button.place(relx=0.5, rely=0.9, anchor='center')
self.counter_label = tk.Label(
self.root, text="", font=("Helvetica", 160), fg="white", bg="black"
)
self.counter_label.place(relx=0.5, rely=0.4, anchor="center")

self.time_label = tk.Label(
self.root, text="", font=("Helvetica", 24), fg="white", bg="black"
)
self.time_label.place(relx=0.5, rely=0.7, anchor="center")

self.quote_label = tk.Label(
self.root,
text="Look 20 feet far away to protect your eyes",
font=("Helvetica", 32),
fg="white",
bg="black",
)
self.quote_label.place(relx=0.5, rely=0.8, anchor="center")

self.skip_button = tk.Button(
self.root,
image=self.button_image,
command=self.skip_reminder,
cursor="hand2",
borderwidth=0,
highlightthickness=0,
relief=tk.FLAT,
activebackground="black",
activeforeground="black",
)
self.skip_button.place(relx=0.5, rely=0.9, anchor="center")

self.create_navigation_buttons()

def create_navigation_buttons(self):
buttons = [
("Donate", "https://www.buymeacoffee.com/nomandhoni"),
("Github", "https://github.com/nomandhoni-cs/blink-eye"),
("Website", "https://blinkeye.vercel.app")
("Website", "https://blinkeye.vercel.app"),
]
for i, (text, link) in enumerate(buttons, start=1):
button = tk.Button(self.root, text=text, font=("Helvetica", 12), fg='white', bg='black', bd=0, cursor='hand2', command=lambda l=link: self.open_link(l))
button.place(relx=0.4 + 0.05 * i, rely=0.95, anchor='center')
button = tk.Button(
self.root,
text=text,
font=("Helvetica", 12),
fg="white",
bg="black",
bd=0,
cursor="hand2",
command=lambda l=link: self.open_link(l),
)
button.place(relx=0.4 + 0.05 * i, rely=0.95, anchor="center")

def skip_reminder(self):
self.root.withdraw()

def fade(self, values: List[int]):
for alphavalue in values:
self.root.attributes("-alpha", alphavalue)
time.sleep(self.FADE_INTERVAL)

def fade_to_black(self, return_to_main: bool = False):
if not return_to_main:
for alphavalue in [i / 10 for i in range(11)]:
self.root.attributes('-alpha', alphavalue)
time.sleep(0.1)
self.fade(self.FADE_VALUES)
else:
values = [i / 10 for i in range(11)]
values.reverse()
for alphavalue in values:
self.root.attributes('-alpha', alphavalue)
time.sleep(0.1)
self.fade(reversed(self.FADE_VALUES))
self.root.withdraw()

def show_timer_popup(self):
while True:
self.root.deiconify()
self.root.attributes("-topmost", True)

current_time = datetime.now().strftime("%I:%M:%S %p")
self.counter_label.config(text="20s")
self.time_label.config(text=current_time)
self.fade_to_black()

for i in range(19, 0, -1):
for i in range(20, 0, -1):
current_time = datetime.now().strftime("%I:%M:%S %p")
self.counter_label.config(text=str(i) + "s")
self.counter_label.config(text=f"{i}s")
self.time_label.config(text=current_time)
if i == 20:
self.fade_to_black()
time.sleep(1)

self.fade_to_black(return_to_main=True)
# Wait for 20 minutes before showing the next popup
time.sleep(1200)
time.sleep(self.POPUP_INTERVAL)

def open_link(self, link):
webbrowser.open(link)
Expand All @@ -110,27 +144,36 @@ def run(self):
title="Blink Eye",
message="Your program has started running.",
app_icon=resource_path("blink-eye-logo.ico"),
timeout=3
timeout=3,
)
# Wait for 20 minutes before showing the first popup
time.sleep(1200)
time.sleep(self.POPUP_INTERVAL)
self.launched_time += 1

threading.Thread(target=self.show_timer_popup).start()
self.root.mainloop()

def exit_action(icon, item):

def exit_action(icon, _):
icon.stop()
os._exit(0)
os._exit(0) # immediate exit


def run_icon():
image = Image.open(resource_path(relative_path='blink-eye-logo.png'))
icon = pystray.Icon("name", image, "Blink Eye", menu=pystray.Menu(item('Exit', exit_action)))
image = Image.open(resource_path(relative_path="blink-eye-logo.png"))
icon = pystray.Icon(
"name",
image,
"Blink Eye",
menu=pystray.Menu(item("Exit", exit_action)),
)
icon.run()


if __name__ == "__main__":
eye_care_app = BlinkEyeApp()
threading.Thread(target=run_icon).start()
try:
eye_care_app = BlinkEyeApp()
eye_care_app.run()
except KeyboardInterrupt:
pass
2 changes: 1 addition & 1 deletion application/build_fedora.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pip install wheel
pip install -r REQUIREMENTS.txt
pip install pyinstaller
echo Building the executable...
pyinstaller --name BlinkEye --onefile --windowed --icon="./Assets/blink-eye-logo.ico" --add-data="./Assets/*;./Assets" --hidden-import plyer.platforms.linux.notification --clean blink-eye.py
pyinstaller --name BlinkEye --onefile --windowed --icon="./Assets/blink-eye-logo.ico" --add-data="./Assets/*;./Assets" --hidden-import plyer.platforms.linux.notification --clean blink_eye.py
echo Building the installer...
rpmdev-setuptree
cp -r ./* ~/rpmbuild/BUILD
Expand Down
4 changes: 3 additions & 1 deletion application/build_windows.bat
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ python -m pip install wheel
python -m pip install -r REQUIREMENTS.txt
python -m pip install pyinstaller
echo Building the executable...
pyinstaller --name BlinkEye --onefile --windowed --icon="./Assets/blink-eye-logo.ico" --add-data="./Assets/*;./Assets" --hidden-import plyer.platforms.win.notification --clean blink-eye.py
pyinstaller --name BlinkEye --onefile --windowed --icon="./Assets/blink-eye-logo.ico" --add-data="./Assets/*;./Assets" --hidden-import plyer.platforms.win.notification --clean blink_eye.py
echo Building the installer...
"%ProgramFiles(x86)%\Inno Setup 6\ISCC.exe" BuildFileForInnoSetup.iss
@REM If the above command does not work, try the following command will be used instead
"C:\Users\%USERNAME%\AppData\Local\Programs\Inno Setup 6\ISCC.exe" BuildFileForInnoSetup.iss
36 changes: 36 additions & 0 deletions application/test.blink-eye.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import unittest
from unittest.mock import call, patch, MagicMock
from blink_eye import BlinkEyeApp, resource_path

class TestBlinkEyeApp(unittest.TestCase):
@patch('blink_eye.tk.PhotoImage')
@patch('blink_eye.tk.Tk')
def test_init(self, mock_tk, mock_photo_image):
mock_photo_image.return_value = MagicMock()
app = BlinkEyeApp()
mock_tk.assert_called_once()
self.assertEqual(app.POPUP_INTERVAL, 1200)
self.assertEqual(app.FADE_INTERVAL, 0.1)
self.assertEqual(app.FADE_VALUES, [i / 10 for i in range(11)])
self.assertEqual(app.launched_time, 0)
self.assertEqual(app.root, mock_tk.return_value)

@patch('blink_eye.webbrowser.open')
@patch('blink_eye.tk.PhotoImage')
@patch('blink_eye.tk.Tk')
def test_open_link(self, mock_tk, mock_photo_image, mock_open):
mock_photo_image.return_value = MagicMock()
app = BlinkEyeApp()
app.open_link('http://example.com')
mock_open.assert_called_once_with('http://example.com')

@patch('blink_eye.tk.PhotoImage')
@patch('blink_eye.tk.Tk')
def test_skip_reminder(self, mock_tk, mock_photo_image):
mock_photo_image.return_value = MagicMock()
app = BlinkEyeApp()
app.skip_reminder()
mock_tk.return_value.withdraw.assert_called_once()

if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion website/configs/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export const CONFIG = {
website: "https://blinkeye.vercel.app",
buymecoffee: "https://www.buymeacoffee.com/nomandhoni",
command:
"pyinstaller --name BlinkEye --onefile --windowed --icon=blink-eye-logo.ico --hidden-import plyer.platforms.win.notification blink-eye.py",
"pyinstaller --name BlinkEye --onefile --windowed --icon=blink-eye-logo.ico --hidden-import plyer.platforms.win.notification blink_eye.py",
};