Skip to content

Commit

Permalink
Merge pull request #1 from freeram/rpctoggle
Browse files Browse the repository at this point in the history
Rpctoggle
  • Loading branch information
jake158 authored May 7, 2024
2 parents 6c5934b + 1b6065a commit eacc4b8
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 15 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
CTkMessagebox==2.5
customtkinter==5.2.2
matplotlib==3.8.3
pygame==2.5.2
Expand Down
2 changes: 1 addition & 1 deletion src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def on_tab_change(self):

class PomodoroApp(ctk.CTk):
WIDTH = 350
HEIGHT = 400
HEIGHT = 450

def __init__(self):
super().__init__()
Expand Down
66 changes: 58 additions & 8 deletions src/frames/pomodoro_frame.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import time
import threading
import customtkinter as ctk
from CTkMessagebox import CTkMessagebox
from datetime import datetime, timedelta
from src.utils import load_config, load_data, save_data, beep, DEF_POMODORO_MINS, DEF_SB_MINS, DEF_LB_MINS, DEF_SB_BEFORE_L
from src.logic.richpresence import RichPresence

BREAK_BTN_COLOR = "#9a9a9a"
BREAK_HOVER = "#adaaaa"

RESET_BTN_COLOR = "#cca508"
RESET_HOVER = "#e3b707"

CONNECTED_TEXT = "Connected to Discord"
CONNECTED_COLOR = "gray65"
CONNECTED_HOVER = "gray77"

DISCONNECTED_TEXT = "Not connected to Discord"
DISCONNECTED_COLOR = "#d93b3b"
DISCONNECTED_HOVER = "#fa5757"


class PomodoroFrame(ctk.CTkFrame):
def __init__(self, master):
Expand Down Expand Up @@ -44,7 +54,19 @@ def initialize_ui(self, config):
self.lb_button.pack(pady=(0, 10))

self.reset_button = ctk.CTkButton(self, text="Reset", font=("Roboto", 17), fg_color=RESET_BTN_COLOR, hover_color=RESET_HOVER, command=self.reset)
self.reset_button.pack()
self.reset_button.pack(pady=(0, 10))

self.discord_button = ctk.CTkButton(self, text="Connecting...", font=("Roboto", 12), fg_color="transparent",
text_color=CONNECTED_COLOR, width=70, command=self.toggle_rpc,
hover_color = self.cget("bg_color"), state="disabled")
# Make text change color on hover
self.discord_button.bind("<Enter>", lambda event: self.discord_button.configure(text_color=CONNECTED_HOVER
if self.discord_button.cget("text") is not DISCONNECTED_TEXT
else DISCONNECTED_HOVER))
self.discord_button.bind("<Leave>", lambda event: self.discord_button.configure(text_color=CONNECTED_COLOR
if self.discord_button.cget("text") is not DISCONNECTED_TEXT
else DISCONNECTED_COLOR))
self.discord_button.pack(pady=(27, 0))

def initialize_state(self, config):
self.running = False
Expand All @@ -70,19 +92,47 @@ def initialize_state(self, config):

def initialize_rpc(self):
self.rpc = RichPresence()
self.discord_button.configure(state="normal", text=CONNECTED_TEXT)
self.rpc_thread = threading.Thread(target=self.update_rpc, daemon=True)
self.rpc_thread.start()

def toggle_rpc(self):
self.discord_button.configure(state="disabled")
if not self.rpc.connected:
self.discord_button.configure(text="Connecting...")
threading.Thread(target=self.connect_rpc, daemon=True).start()
else:
self.discord_button.configure(text="Disconnecting...")
threading.Thread(target=self.disconnect_rpc, daemon=True).start()

def connect_rpc(self):
if self.rpc.connect():
self.discord_button.configure(text=CONNECTED_TEXT, text_color=CONNECTED_COLOR, state="normal")
else:
self.discord_button.configure(text=DISCONNECTED_TEXT, text_color=DISCONNECTED_COLOR, state="normal")
CTkMessagebox(title="Error", message="Connecting to Discord failed\nCheck console for error output", icon="cancel")

def disconnect_rpc(self):
if self.rpc.disconnect():
self.discord_button.configure(text=DISCONNECTED_TEXT, text_color=DISCONNECTED_COLOR, state="normal")
else:
self.discord_button.configure(text=CONNECTED_TEXT, text_color=CONNECTED_COLOR, state="normal")
CTkMessagebox(title="Error", message="Disconnecting from Discord failed\nCheck console for error output", icon="cancel")

def update_rpc(self):
while True:
if self.break_running:
self.rpc.break_state(self.seconds_studied, self.start_time_timestamp, self.end_time_timestamp)
elif self.running:
self.rpc.running_state(self.session_counter + 1, self.start_time_timestamp, self.end_time_timestamp)
elif self.paused:
self.rpc.paused_state(self.start_time_timestamp)
if self.rpc.connected:
if self.break_running:
self.rpc.break_state(self.seconds_studied, self.start_time_timestamp, self.end_time_timestamp)
elif self.running:
self.rpc.running_state(self.session_counter + 1, self.start_time_timestamp, self.end_time_timestamp)
elif self.paused:
self.rpc.paused_state(self.start_time_timestamp)
else:
self.rpc.idling_state(self.seconds_studied)
else:
self.rpc.idling_state()
if self.discord_button.cget("state") == 'normal':
self.discord_button.configure(text=DISCONNECTED_TEXT, text_color=DISCONNECTED_COLOR)

# Discord-imposed rate limit
time.sleep(15)
Expand Down
13 changes: 13 additions & 0 deletions src/frames/stats_frame.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import customtkinter as ctk
from datetime import datetime
from CTkMessagebox import CTkMessagebox
from src.reusable.stats_reusable import StatisticFrame, ButtonFrame
from src.utils import load_data
from src.logic.graphs import graph_pomodoro_sessions, graph_hours_studied
Expand Down Expand Up @@ -51,8 +52,20 @@ def update_total_pomodoros(self, data):
total_pomodoros = data.get('total_pomodoro_sessions', 0)
self.total_pomodoros.set_value(f"{total_pomodoros} session{'s' if total_pomodoros != 1 else ''}")

def _ensure_exists(self, param, msg):
data = load_data()
value = data.get(param, 0)
if value == 0:
CTkMessagebox(title="Error", message=msg, icon="cancel")
return False
return True

def show_sessions_graph(self):
if not self._ensure_exists('total_pomodoro_sessions', 'Cannot graph pomodoro sessions: none recorded'):
return
graph_pomodoro_sessions(load_data())

def show_hours_graph(self):
if not self._ensure_exists('total_seconds_studied', 'Cannot graph hours studied: none recorded'):
return
graph_hours_studied(load_data())
46 changes: 40 additions & 6 deletions src/logic/richpresence.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pypresence
from datetime import datetime
from src.utils import load_data

CLIENT_ID = '1215345125002059836'

Expand All @@ -9,33 +10,66 @@ def __init__(self):
super().__init__(client_id=CLIENT_ID)
self.launch_time = datetime.now().timestamp()
# Can only update every 15 seconds
self.connected = False
self.connect()
self.total_seconds_studied = load_data().get('total_seconds_studied', 0)
self.idling_state()

def _handle_exceptions(func):
def wrapper(self, *args, **kwargs):
if not self.connected:
return
try:
func(self, *args, **kwargs)
except Exception as e:
print(f"Error updating Rich Presence: {e}")
self.connected = False
return wrapper

def connect(self):
try:
self.connect()
self.idling_state()
super().connect()
self.connected = True
return True
except Exception as e:
print(f"Failed to connect to Discord: {e}")
self.connected = False
return False

def disconnect(self):
try:
super().close()
self.connected = False
return True
except Exception as e:
print(f"Failed to disconnect from Discord: {e}")
self.connected = True
return False

def format_time(self, seconds_studied):
total_seconds = seconds_studied
total_hours = seconds_studied / 3600

if total_hours < 1:
return f"{total_seconds // 60} minute{'s' if total_seconds // 60 != 1 else ''}"
else:
return f"{round(total_hours, 1) if total_hours % 1 != 0 else int(total_hours)} hours"

def idling_state(self):
self.update(state="Idling", details=None, start=self.launch_time, large_image="graytomato",
large_text="github.com/freeram/pomodoro-discord")
@_handle_exceptions
def idling_state(self, seconds_studied=0):
self.update(state=f"Total time studied: {self.format_time(self.total_seconds_studied + seconds_studied)}",
details="Idling", start=self.launch_time, large_image="graytomato", large_text="github.com/freeram/pomodoro-discord")

@_handle_exceptions
def running_state(self, session, start_time, end_time):
self.update(state=f"Session {session}", details="Studying", start=start_time,
end=end_time, large_image="tomato", large_text="github.com/freeram/pomodoro-discord")

@_handle_exceptions
def paused_state(self, start_time):
self.update(state="Paused", details=None, start=start_time, large_image="graytomato",
large_text="github.com/freeram/pomodoro-discord")

@_handle_exceptions
def break_state(self, seconds_studied, start_time, end_time):
self.update(state=f"Time studied: {self.format_time(seconds_studied)}", details="On break", start=start_time,
end=end_time, large_image="greentomato", large_text="github.com/freeram/pomodoro-discord")

0 comments on commit eacc4b8

Please sign in to comment.