mirror of
https://github.com/ApfelTeeSaft/SplitNotes.git
synced 2026-08-27 11:53:28 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91d71308df | ||
|
|
6e30be4b4a | ||
|
|
a49e0934af | ||
|
|
fe83570cec |
@@ -3,7 +3,7 @@ Software for syncing notes with LiveSplit using the LiveSplit server component.
|
|||||||
|
|
||||||
Splitnotes automatically shows notes for the split you are currently on.
|
Splitnotes automatically shows notes for the split you are currently on.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
@@ -45,12 +45,18 @@ Example:
|
|||||||
2. Choose your text file.
|
2. Choose your text file.
|
||||||
3. Make sure that notes for the right amount of splits have been loaded.
|
3. Make sure that notes for the right amount of splits have been loaded.
|
||||||
|
|
||||||
|
**Change Settings**
|
||||||
|
1. Right-Click in SplitNotes and select "Settings"
|
||||||
|
2. The settings menu will show up. Here you can set things like font, font size, text and background color and the server port.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Automatically displayed notes based on the active split in LiveSplit.
|
* Automatically displayed notes based on the active split in LiveSplit.
|
||||||
* Ability to preview notes in the software when no run is going on by using the right and left arrow keys.
|
* Ability to preview notes in the software when no run is going on by using the right and left arrow keys.
|
||||||
* Two different font sizes to make sure that notes are easy to read.
|
* Two different font sizes to make sure that notes are easy to read.
|
||||||
* Double layout to preview notes for both current and next split.
|
* Double layout to preview notes for both current and next split.
|
||||||
|
* Multiple settings to change the look of the text.
|
||||||
|
* Ability to set a custom port for interaction with the LiveSplit Server.
|
||||||
|
|
||||||
#### Development
|
#### Development
|
||||||
Written in mainly procedural Python using tkinter GUI library.
|
Written in mainly procedural Python using tkinter GUI library.
|
||||||
|
|||||||
Binary file not shown.
@@ -44,7 +44,8 @@ MENU_OPTIONS = {
|
|||||||
# Error messages
|
# Error messages
|
||||||
ERRORS = {"NOTES_EMPTY": ("Error!", "Notes empty or can't be loaded!"),
|
ERRORS = {"NOTES_EMPTY": ("Error!", "Notes empty or can't be loaded!"),
|
||||||
"FONT_SIZE": ("Error!", "Invalid Font Size!"),
|
"FONT_SIZE": ("Error!", "Invalid Font Size!"),
|
||||||
"SERVER_PORT": ("Error!", "Invalid server port!")}
|
"SERVER_PORT": ("Error!", "Invalid server port!"),
|
||||||
|
"SEPARATOR": ("Error!", "Invalid split separator!")}
|
||||||
|
|
||||||
# Max file size for notes
|
# Max file size for notes
|
||||||
MAX_FILE_SIZE = 1000000000 # 1 Giga-Byte
|
MAX_FILE_SIZE = 1000000000 # 1 Giga-Byte
|
||||||
@@ -62,7 +63,9 @@ TEXT_FILES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Default content of config.cfg file
|
# Default content of config.cfg file
|
||||||
DEFAULT_CONFIG = "notes=\nfont_size=12\nfont=arial\ntext_color=#000000\nbackground_color=#FFFFFF\ndouble_layout=False\nserver_port=16834\nwidth=400\nheight=300"
|
DEFAULT_CONFIG = "notes=\nfont_size=12\nfont=arial\ntext_color=#000000\nbackground_color=#FFFFFF\ndouble_layout=False\nserver_port=16834\nwidth=400\nheight=300\nseparator=new_line"
|
||||||
|
|
||||||
|
NEWLINE_CONSTANT = "new_line"
|
||||||
|
|
||||||
# Required settings
|
# Required settings
|
||||||
REQUIRED_SETTINGS = ("notes",
|
REQUIRED_SETTINGS = ("notes",
|
||||||
@@ -73,13 +76,14 @@ REQUIRED_SETTINGS = ("notes",
|
|||||||
"server_port",
|
"server_port",
|
||||||
"double_layout",
|
"double_layout",
|
||||||
"width",
|
"width",
|
||||||
"height"
|
"height",
|
||||||
|
"separator"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Settings window options
|
# Settings window options
|
||||||
SETTINGS_WINDOW = {"TITLE": "Settings",
|
SETTINGS_WINDOW = {"TITLE": "Settings",
|
||||||
"WIDTH": 360,
|
"WIDTH": 360,
|
||||||
"HEIGHT": 330,
|
"HEIGHT": 410,
|
||||||
"CANCEL": "Cancel",
|
"CANCEL": "Cancel",
|
||||||
"SAVE": "Save"}
|
"SAVE": "Save"}
|
||||||
|
|
||||||
@@ -90,7 +94,9 @@ SETTINGS_OPTIONS = {"FONT": "Font",
|
|||||||
"BG_COLOR": "Background Color",
|
"BG_COLOR": "Background Color",
|
||||||
"SERVER_PORT": "LiveSplit Server port",
|
"SERVER_PORT": "LiveSplit Server port",
|
||||||
"DEFAULT_SERVER_PORT": "(Default is 16834)",
|
"DEFAULT_SERVER_PORT": "(Default is 16834)",
|
||||||
"DOUBLE_LAYOUT": "Use double layout"}
|
"DOUBLE_LAYOUT": "Use double layout",
|
||||||
|
"NEW_LINE_SEPARATOR": "Newline as split separator",
|
||||||
|
"CUSTOM_SEPARATOR": "Custom split separator"}
|
||||||
|
|
||||||
# Fonts that can be selected
|
# Fonts that can be selected
|
||||||
AVAILABLE_FONTS = ("arial",
|
AVAILABLE_FONTS = ("arial",
|
||||||
@@ -100,4 +106,4 @@ AVAILABLE_FONTS = ("arial",
|
|||||||
"ms serif",
|
"ms serif",
|
||||||
"system",
|
"system",
|
||||||
"times new roman",
|
"times new roman",
|
||||||
"verdana")
|
"verdana")
|
||||||
+33
-7
@@ -16,7 +16,8 @@ runtime_info = {
|
|||||||
"notes": [],
|
"notes": [],
|
||||||
"server_port": 0,
|
"server_port": 0,
|
||||||
"force_reset": False,
|
"force_reset": False,
|
||||||
"double_layout": False
|
"double_layout": False,
|
||||||
|
"settings": {}
|
||||||
}
|
}
|
||||||
|
|
||||||
root = tkinter.Tk()
|
root = tkinter.Tk()
|
||||||
@@ -219,7 +220,7 @@ def load_notes(window, text1, text2, com_socket):
|
|||||||
file = noter.select_file()
|
file = noter.select_file()
|
||||||
|
|
||||||
if file:
|
if file:
|
||||||
notes = noter.get_notes(file)
|
notes = noter.get_notes(file, runtime_info["settings"]["separator"])
|
||||||
if notes:
|
if notes:
|
||||||
# Notes loaded correctly
|
# Notes loaded correctly
|
||||||
runtime_info["notes"] = notes
|
runtime_info["notes"] = notes
|
||||||
@@ -334,7 +335,7 @@ def set_title_notes(window, index, split_name=False):
|
|||||||
update_title(title, window)
|
update_title(title, window)
|
||||||
|
|
||||||
|
|
||||||
def menu_open_settings(root_wnd, box1, box2, text1, text2):
|
def menu_open_settings(root_wnd, box1, box2, text1, text2, com_socket):
|
||||||
"""
|
"""
|
||||||
Opens the settings menu.
|
Opens the settings menu.
|
||||||
"""
|
"""
|
||||||
@@ -342,14 +343,16 @@ def menu_open_settings(root_wnd, box1, box2, text1, text2):
|
|||||||
(lambda settings: apply_settings(settings,
|
(lambda settings: apply_settings(settings,
|
||||||
root_wnd,
|
root_wnd,
|
||||||
box1, box2,
|
box1, box2,
|
||||||
text1, text2)))
|
text1, text2, com_socket)))
|
||||||
|
|
||||||
|
|
||||||
def apply_settings(settings, window, box1, box2, text1, text2):
|
def apply_settings(settings, window, box1, box2, text1, text2, com_socket):
|
||||||
"""
|
"""
|
||||||
Applies the given settings to the given components.
|
Applies the given settings to the given components.
|
||||||
Settings must be a correctly formatted dictionary.
|
Settings must be a correctly formatted dictionary.
|
||||||
"""
|
"""
|
||||||
|
runtime_info["settings"] = settings
|
||||||
|
|
||||||
# Server port change
|
# Server port change
|
||||||
if not (runtime_info["server_port"] == int(settings["server_port"])):
|
if not (runtime_info["server_port"] == int(settings["server_port"])):
|
||||||
runtime_info["server_port"] = int(settings["server_port"])
|
runtime_info["server_port"] = int(settings["server_port"])
|
||||||
@@ -367,6 +370,28 @@ def apply_settings(settings, window, box1, box2, text1, text2):
|
|||||||
text1.config(fg=settings["text_color"], bg=settings["background_color"])
|
text1.config(fg=settings["text_color"], bg=settings["background_color"])
|
||||||
text2.config(fg=settings["text_color"], bg=settings["background_color"])
|
text2.config(fg=settings["text_color"], bg=settings["background_color"])
|
||||||
|
|
||||||
|
old_note_length = len(runtime_info["notes"])
|
||||||
|
|
||||||
|
if settings["notes"] and noter.file_exists(settings["notes"]):
|
||||||
|
new_notes = noter.get_notes(settings["notes"], settings["separator"])
|
||||||
|
|
||||||
|
if new_notes:
|
||||||
|
# Notes loaded correctly
|
||||||
|
runtime_info["notes"] = new_notes
|
||||||
|
|
||||||
|
new_note_length = len(new_notes)
|
||||||
|
|
||||||
|
if not (new_note_length == old_note_length):
|
||||||
|
show_info(("Notes Loaded",
|
||||||
|
("Loaded notes with " + str(new_note_length) + " splits.")))
|
||||||
|
|
||||||
|
if not runtime_info["timer_running"]:
|
||||||
|
runtime_info["active_split"] = -1
|
||||||
|
|
||||||
|
update_GUI(window, com_socket, text1, text2)
|
||||||
|
else:
|
||||||
|
show_info(config.ERRORS["NOTES_EMPTY"], True)
|
||||||
|
|
||||||
|
|
||||||
def save_geometry_settings(width, height):
|
def save_geometry_settings(width, height):
|
||||||
"""
|
"""
|
||||||
@@ -397,6 +422,7 @@ def init_UI(root):
|
|||||||
# Load Settings
|
# Load Settings
|
||||||
settings = setting_handler.load_settings()
|
settings = setting_handler.load_settings()
|
||||||
runtime_info["server_port"] = int(settings["server_port"])
|
runtime_info["server_port"] = int(settings["server_port"])
|
||||||
|
runtime_info["settings"] = settings
|
||||||
|
|
||||||
# Graphical components
|
# Graphical components
|
||||||
root.geometry(settings["width"] + "x" + settings["height"])
|
root.geometry(settings["width"] + "x" + settings["height"])
|
||||||
@@ -453,7 +479,7 @@ def init_UI(root):
|
|||||||
)
|
)
|
||||||
popup.add_command(
|
popup.add_command(
|
||||||
label=config.MENU_OPTIONS["SETTINGS"],
|
label=config.MENU_OPTIONS["SETTINGS"],
|
||||||
command=(lambda: menu_open_settings(root, box1, box2, text1, text2))
|
command=(lambda: menu_open_settings(root, box1, box2, text1, text2, com_socket))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set default window icon and title
|
# Set default window icon and title
|
||||||
@@ -464,7 +490,7 @@ def init_UI(root):
|
|||||||
settings = setting_handler.load_settings()
|
settings = setting_handler.load_settings()
|
||||||
|
|
||||||
if settings["notes"] and noter.file_exists(settings["notes"]):
|
if settings["notes"] and noter.file_exists(settings["notes"]):
|
||||||
notes = noter.get_notes(settings["notes"])
|
notes = noter.get_notes(settings["notes"], settings["separator"])
|
||||||
|
|
||||||
if notes:
|
if notes:
|
||||||
runtime_info["notes"] = notes
|
runtime_info["notes"] = notes
|
||||||
|
|||||||
+17
-10
@@ -5,9 +5,10 @@ import config
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
NOTE STANDARD FORMATTING
|
NOTE STANDARD FORMATTING
|
||||||
|
|
||||||
empty newlines separate notes for different splits
|
empty newlines separate notes for different splits
|
||||||
|
|
||||||
|
It is also possible to set your own split separator in the settings menu
|
||||||
|
|
||||||
lines that start and end with [ ] are ignored for notes.
|
lines that start and end with [ ] are ignored for notes.
|
||||||
these can be used for titles.
|
these can be used for titles.
|
||||||
(ex. [Split1] is not included in notes)
|
(ex. [Split1] is not included in notes)
|
||||||
@@ -50,21 +51,29 @@ def get_note_lines(file_path):
|
|||||||
return f_lines
|
return f_lines
|
||||||
|
|
||||||
|
|
||||||
def encode_notes(note_lines):
|
def decode_notes(note_lines, separator):
|
||||||
"""
|
"""
|
||||||
Takes a list containing strings.
|
Takes a list containing strings.
|
||||||
Encodes given strings according to the note formatting.
|
Encodes given strings according to the note formatting.
|
||||||
Returns the list containing the notes for every split.
|
Returns the list containing the notes for every split.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
#Check if newline is being used as separator
|
||||||
|
|
||||||
|
if separator == config.NEWLINE_CONSTANT:
|
||||||
|
separator = "" # left after stripping newline
|
||||||
|
|
||||||
def is_title(line):
|
def is_title(line):
|
||||||
if not line:
|
if not line:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return (line[0] == "[") and (line[-1] == "]")
|
return (line[0] == "[") and (line[-1] == "]")
|
||||||
|
|
||||||
def is_newline(line):
|
def is_separator(line):
|
||||||
return (line == "\n") or (line == "\r")
|
return (line == separator)
|
||||||
|
|
||||||
|
def is_newline(s):
|
||||||
|
return (s == "\n")
|
||||||
|
|
||||||
def remove_new_line(line):
|
def remove_new_line(line):
|
||||||
if (len(line) >= 1) and (is_newline(line[-1])):
|
if (len(line) >= 1) and (is_newline(line[-1])):
|
||||||
@@ -76,15 +85,13 @@ def encode_notes(note_lines):
|
|||||||
cur_notes = ""
|
cur_notes = ""
|
||||||
|
|
||||||
for line in note_lines:
|
for line in note_lines:
|
||||||
# remove whitespace at beginning and end
|
line = remove_new_line(line)
|
||||||
line = line.strip(" ")
|
|
||||||
|
|
||||||
if is_newline(line):
|
if is_separator(line):
|
||||||
if cur_notes:
|
if cur_notes:
|
||||||
note_list.append(cur_notes)
|
note_list.append(cur_notes)
|
||||||
cur_notes = ""
|
cur_notes = ""
|
||||||
else:
|
else:
|
||||||
line = remove_new_line(line)
|
|
||||||
if not is_title(line):
|
if not is_title(line):
|
||||||
cur_notes += line + "\n" # newline
|
cur_notes += line + "\n" # newline
|
||||||
|
|
||||||
@@ -94,7 +101,7 @@ def encode_notes(note_lines):
|
|||||||
return note_list
|
return note_list
|
||||||
|
|
||||||
|
|
||||||
def get_notes(file_path):
|
def get_notes(file_path, separator):
|
||||||
"""
|
"""
|
||||||
Takes a path to a file and returns a list with the notes
|
Takes a path to a file and returns a list with the notes
|
||||||
in the file encoded according to the note fromatting.
|
in the file encoded according to the note fromatting.
|
||||||
@@ -106,7 +113,7 @@ def get_notes(file_path):
|
|||||||
if not note_lines:
|
if not note_lines:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
note_list = encode_notes(note_lines)
|
note_list = decode_notes(note_lines, separator)
|
||||||
|
|
||||||
return note_list
|
return note_list
|
||||||
|
|
||||||
|
|||||||
+61
-11
@@ -127,6 +127,9 @@ def validate_settings(settings):
|
|||||||
if not validate_pixels(settings["height"]):
|
if not validate_pixels(settings["height"]):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not validate_separator(settings["separator"]):
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -171,6 +174,12 @@ def edit_settings(root_wnd, apply_method):
|
|||||||
layout_label = tkinter.Label(settings_wnd,
|
layout_label = tkinter.Label(settings_wnd,
|
||||||
text=config.SETTINGS_OPTIONS["DOUBLE_LAYOUT"],
|
text=config.SETTINGS_OPTIONS["DOUBLE_LAYOUT"],
|
||||||
font=config.GUI_FONT)
|
font=config.GUI_FONT)
|
||||||
|
newline_label = tkinter.Label(settings_wnd,
|
||||||
|
text=config.SETTINGS_OPTIONS["NEW_LINE_SEPARATOR"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
separator_label = tkinter.Label(settings_wnd,
|
||||||
|
text=config.SETTINGS_OPTIONS["CUSTOM_SEPARATOR"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
port_label = tkinter.Label(settings_wnd,
|
port_label = tkinter.Label(settings_wnd,
|
||||||
text=config.SETTINGS_OPTIONS["SERVER_PORT"],
|
text=config.SETTINGS_OPTIONS["SERVER_PORT"],
|
||||||
font=config.GUI_FONT)
|
font=config.GUI_FONT)
|
||||||
@@ -243,6 +252,28 @@ def edit_settings(root_wnd, apply_method):
|
|||||||
if decode_boolean_setting(settings["double_layout"]):
|
if decode_boolean_setting(settings["double_layout"]):
|
||||||
double_layout_btn.select()
|
double_layout_btn.select()
|
||||||
|
|
||||||
|
# Separator selection
|
||||||
|
separator_entry = tkinter.Entry(settings_wnd, width=14, font=config.GUI_FONT)
|
||||||
|
|
||||||
|
def set_separator_active(active):
|
||||||
|
if active:
|
||||||
|
separator_entry.configure(state="normal")
|
||||||
|
else:
|
||||||
|
separator_entry.configure(state="disabled")
|
||||||
|
|
||||||
|
use_newline = tkinter.BooleanVar()
|
||||||
|
newline_btn = tkinter.Checkbutton(settings_wnd,
|
||||||
|
variable=use_newline,
|
||||||
|
command=
|
||||||
|
(lambda: set_separator_active(not use_newline.get()))
|
||||||
|
)
|
||||||
|
|
||||||
|
if settings["separator"] == config.NEWLINE_CONSTANT:
|
||||||
|
newline_btn.select()
|
||||||
|
set_separator_active(False);
|
||||||
|
else:
|
||||||
|
separator_entry.insert(0, settings["separator"])
|
||||||
|
|
||||||
# Save and cancel buttons
|
# Save and cancel buttons
|
||||||
def control_and_save():
|
def control_and_save():
|
||||||
errors_found = False
|
errors_found = False
|
||||||
@@ -252,6 +283,11 @@ def edit_settings(root_wnd, apply_method):
|
|||||||
chosen_font_size = font_size_entry.get()
|
chosen_font_size = font_size_entry.get()
|
||||||
chosen_port = port_entry.get()
|
chosen_port = port_entry.get()
|
||||||
|
|
||||||
|
if use_newline.get():
|
||||||
|
chosen_separator = config.NEWLINE_CONSTANT
|
||||||
|
else:
|
||||||
|
chosen_separator = separator_entry.get()
|
||||||
|
|
||||||
settings["double_layout"] = encode_boolean_setting(double_layout.get())
|
settings["double_layout"] = encode_boolean_setting(double_layout.get())
|
||||||
|
|
||||||
if not validate_font_size(chosen_font_size):
|
if not validate_font_size(chosen_font_size):
|
||||||
@@ -266,6 +302,12 @@ def edit_settings(root_wnd, apply_method):
|
|||||||
else:
|
else:
|
||||||
settings["server_port"] = chosen_port
|
settings["server_port"] = chosen_port
|
||||||
|
|
||||||
|
if not validate_separator(chosen_separator):
|
||||||
|
msgbox.showerror(config.ERRORS["SEPARATOR"][0], config.ERRORS["SEPARATOR"][1])
|
||||||
|
errors_found = True
|
||||||
|
else:
|
||||||
|
settings["separator"] = chosen_separator
|
||||||
|
|
||||||
if not errors_found:
|
if not errors_found:
|
||||||
save_settings(settings)
|
save_settings(settings)
|
||||||
apply_method(settings)
|
apply_method(settings)
|
||||||
@@ -288,18 +330,22 @@ def edit_settings(root_wnd, apply_method):
|
|||||||
text_color_label.place(x=15, y=95)
|
text_color_label.place(x=15, y=95)
|
||||||
bg_color_label.place(x=15, y=135)
|
bg_color_label.place(x=15, y=135)
|
||||||
layout_label.place(x=15, y=175)
|
layout_label.place(x=15, y=175)
|
||||||
port_label.place(x=15, y=215)
|
newline_label.place(x=15, y=215)
|
||||||
default_port_label.place(x=15, y=240)
|
separator_label.place(x=15, y=240)
|
||||||
|
port_label.place(x=15, y=280)
|
||||||
|
default_port_label.place(x=15, y=305)
|
||||||
|
|
||||||
font_dropdown.place(x=178, y=15)
|
font_dropdown.place(x=208, y=15)
|
||||||
font_size_entry.place(x=180, y=55)
|
font_size_entry.place(x=210, y=55)
|
||||||
text_color.place(x=180, y=95)
|
text_color.place(x=210, y=95)
|
||||||
bg_color.place(x=180, y=135)
|
bg_color.place(x=210, y=135)
|
||||||
double_layout_btn.place(x=180, y=175)
|
double_layout_btn.place(x=210, y=175)
|
||||||
port_entry.place(x=180, y=215)
|
newline_btn.place(x=210, y=215)
|
||||||
|
separator_entry.place(x=210, y=240)
|
||||||
|
port_entry.place(x=210, y=280)
|
||||||
|
|
||||||
save_btn.place(x=110, y=280)
|
save_btn.place(x=110, y=350)
|
||||||
cancel_btn.place(x=190, y=280)
|
cancel_btn.place(x=190, y=350)
|
||||||
|
|
||||||
|
|
||||||
def validate_color(color):
|
def validate_color(color):
|
||||||
@@ -365,7 +411,7 @@ def encode_boolean_setting(value):
|
|||||||
def validate_pixels(pixels):
|
def validate_pixels(pixels):
|
||||||
"""
|
"""
|
||||||
Checks if given string can be used as a pixel value for height or width.
|
Checks if given string can be used as a pixel value for height or width.
|
||||||
Height or Width ar assumed to never surpass 10000
|
Height or Width or assumed to never surpass 10000
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
pixels = int(pixels)
|
pixels = int(pixels)
|
||||||
@@ -373,3 +419,7 @@ def validate_pixels(pixels):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return 0 < pixels < 10000
|
return 0 < pixels < 10000
|
||||||
|
|
||||||
|
|
||||||
|
def validate_separator(separator):
|
||||||
|
return separator.strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user