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 | ||
|
|
4037122692 | ||
|
|
b74903daf4 | ||
|
|
2d56e617c0 | ||
|
|
d314860608 | ||
|
|
af125173c4 | ||
|
|
c3e7c275f7 | ||
|
|
c09cb1603c | ||
|
|
f513cef7af | ||
|
|
917e65f7c1 | ||
|
|
99e8f606b9 | ||
|
|
07c336782a |
+5
-1
@@ -1 +1,5 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
|
TESTING.py
|
||||||
|
.idea
|
||||||
|
__pycache__
|
||||||
|
resources/config.cfg
|
||||||
@@ -3,6 +3,9 @@ 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
|
||||||
1. Download the latest version of LiveSplit Server Component from [this](https://github.com/LiveSplit/LiveSplit.Server/releases) site.
|
1. Download the latest version of LiveSplit Server Component from [this](https://github.com/LiveSplit/LiveSplit.Server/releases) site.
|
||||||
2. Unzip and move the files to the component folder in your LiveSplit install (...\LiveSplit\Components)
|
2. Unzip and move the files to the component folder in your LiveSplit install (...\LiveSplit\Components)
|
||||||
@@ -11,9 +14,9 @@ Splitnotes automatically shows notes for the split you are currently on.
|
|||||||
|
|
||||||
## How To Use
|
## How To Use
|
||||||
**Connect to LiveSplit**
|
**Connect to LiveSplit**
|
||||||
1. Launch Splitnotes
|
1. Launch Splitnotes.exe
|
||||||
2. Launch LiveSplit
|
2. Launch LiveSplit
|
||||||
3. Go to "Edit Layout" -> "+" -> "Control" -> "LiveSplit Server". Hit ok.
|
3. Go to "Edit Layout" -> "+" -> "Control" -> "LiveSplit Server". Hit ok. (Let the server use the default port: 16834)
|
||||||
4. In LiveSplit, select "Control" -> "Start Server".
|
4. In LiveSplit, select "Control" -> "Start Server".
|
||||||
5. SplitNotes should now be connected to LiveSplit. If connection is active the Icon for SplitNotes is green.
|
5. SplitNotes should now be connected to LiveSplit. If connection is active the Icon for SplitNotes is green.
|
||||||
|
|
||||||
@@ -35,20 +38,26 @@ Example:
|
|||||||
>A simple new line is enough to separate notes for different splits.
|
>A simple new line is enough to separate notes for different splits.
|
||||||
>
|
>
|
||||||
>Also som notes for split 3.
|
>Also som notes for split 3.
|
||||||
>You don't have to [ be afraid to use brackets [ in the middle of notes].
|
>You don't have to [ worry abut using brackets [ in the middle of a row].
|
||||||
|
|
||||||
**Load Notes**
|
**Load Notes**
|
||||||
1. Right-Click in SplitNotes and select "Load Notes".
|
1. Right-Click in SplitNotes and select "Load Notes".
|
||||||
2. Choose your text file.
|
2. Choose your text file.
|
||||||
3. Make sure that notes for the right number of splits has 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 before 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 Python using tkinter GUI library.
|
Written in mainly procedural Python using tkinter GUI library.
|
||||||
Made by Joelnir.
|
Made by Joelnir.
|
||||||
|
|||||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
|||||||
import tkinter
|
|
||||||
|
|
||||||
tkinter.messagebox.showinfo("hi!")
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,64 +1,109 @@
|
|||||||
#CONFIG FILE WITH CONSTANTS
|
# CONFIG FILE WITH CONSTANTS
|
||||||
|
|
||||||
DEBUG = True
|
# Livesplit connection
|
||||||
|
|
||||||
#Livesplit connection
|
|
||||||
HOST = "localhost"
|
HOST = "localhost"
|
||||||
PORT = 16834
|
PORT = 16834
|
||||||
|
|
||||||
#In network communication, time out after this time. (in seconds)
|
# In network communication, time out after this time. (in seconds)
|
||||||
COM_TIMEOUT = 0.5
|
COM_TIMEOUT = 0.5
|
||||||
|
|
||||||
#Possible commands to send to LiveSplit
|
# Possible commands to send to LiveSplit
|
||||||
LS_COMMANDS = {
|
LS_COMMANDS = {
|
||||||
"best_possible": "getbestpossibletime\r\n",
|
"best_possible": "getbestpossibletime\r\n",
|
||||||
"cur_split_index": "getsplitindex\r\n",
|
"cur_split_index": "getsplitindex\r\n",
|
||||||
"cur_split_name": "getcurrentsplitname\r\n"
|
"cur_split_name": "getcurrentsplitname\r\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
#Default Window Size
|
# Default Window Settings
|
||||||
DEFAULT_WINDOW = {"WIDTH": 400, "HEIGHT": 300, "TITLE": "SplitNotes"}
|
DEFAULT_WINDOW = {"TITLE": "SplitNotes"}
|
||||||
|
|
||||||
#Color Scheme
|
# Default Welcome Message
|
||||||
COLOR_SCHEME = {}
|
|
||||||
|
|
||||||
#Default Welcome Message
|
|
||||||
DEFAULT_MSG = "Right Click to Open Notes."
|
DEFAULT_MSG = "Right Click to Open Notes."
|
||||||
|
|
||||||
#Update time for polling livesplit and other actions (in seconds)
|
# Update time for polling livesplit and other actions (in seconds)
|
||||||
POLLING_TIME = 0.5
|
POLLING_TIME = 0.5
|
||||||
|
|
||||||
#file names and path of icons
|
# File names and path for resources
|
||||||
ICON_FOLDER = "resources"
|
RESOURCE_FOLDER = "resources"
|
||||||
ICONS= {"GREEN": "green.png", "RED": "red.png"}
|
ICONS = {"GREEN": "green.png", "RED": "red.png", "SETTINGS": "settings_icon.png"}
|
||||||
|
SETTINGS_FILE = "config.cfg"
|
||||||
|
|
||||||
#Popup menu options
|
# Default Scrollbar Width
|
||||||
|
SCROLLBAR_WIDTH = 16
|
||||||
|
|
||||||
|
# Popup menu options
|
||||||
MENU_OPTIONS = {
|
MENU_OPTIONS = {
|
||||||
"SINGLE": "Set Single Layout",
|
"SINGLE": "Set Single Layout",
|
||||||
"DOUBLE": "Set Double Layout",
|
"DOUBLE": "Set Double Layout",
|
||||||
"LOAD": "Load Notes",
|
"LOAD": "Load Notes",
|
||||||
"BIG": "Big Font",
|
"BIG": "Big Font",
|
||||||
"SMALL": "Small Font"
|
"SMALL": "Small Font",
|
||||||
}
|
"SETTINGS": "Settings"
|
||||||
|
}
|
||||||
|
|
||||||
#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!"),
|
||||||
|
"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
|
||||||
|
|
||||||
#TO be added to title to alert user that timer is running
|
# To be added to title to alert user that timer is running
|
||||||
RUNNING_ALERT = "RUNNING"
|
RUNNING_ALERT = "RUNNING"
|
||||||
|
|
||||||
#Font for notes
|
# Font for gui widgets
|
||||||
FONT = {"NAME": "Arial", "SMALL": 12, "BIG": 16}
|
GUI_FONT = ("arial", 12)
|
||||||
|
|
||||||
#Color scheme
|
# Files tht should be displayed and opened a notes
|
||||||
COLOR = {"TEXT": "black", "TEXT_BG": "ivory"}
|
|
||||||
|
|
||||||
#Files tht should be displayed and opened a notes
|
|
||||||
TEXT_FILES = [
|
TEXT_FILES = [
|
||||||
("Text Files", ("*.txt", "*.log", "*.asc", "*.conf", "*.cfg")),
|
("Text Files", ("*.txt", "*.log", "*.asc", "*.conf", "*.cfg")),
|
||||||
('All','*')
|
('All', '*')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# 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\nseparator=new_line"
|
||||||
|
|
||||||
|
NEWLINE_CONSTANT = "new_line"
|
||||||
|
|
||||||
|
# Required settings
|
||||||
|
REQUIRED_SETTINGS = ("notes",
|
||||||
|
"font",
|
||||||
|
"font_size",
|
||||||
|
"text_color",
|
||||||
|
"background_color",
|
||||||
|
"server_port",
|
||||||
|
"double_layout",
|
||||||
|
"width",
|
||||||
|
"height",
|
||||||
|
"separator"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Settings window options
|
||||||
|
SETTINGS_WINDOW = {"TITLE": "Settings",
|
||||||
|
"WIDTH": 360,
|
||||||
|
"HEIGHT": 410,
|
||||||
|
"CANCEL": "Cancel",
|
||||||
|
"SAVE": "Save"}
|
||||||
|
|
||||||
|
# OPTIONS IN THE SETTINGS WINDOW
|
||||||
|
SETTINGS_OPTIONS = {"FONT": "Font",
|
||||||
|
"FONT_SIZE": "Font Size",
|
||||||
|
"TEXT_COLOR": "Text Color",
|
||||||
|
"BG_COLOR": "Background Color",
|
||||||
|
"SERVER_PORT": "LiveSplit Server port",
|
||||||
|
"DEFAULT_SERVER_PORT": "(Default is 16834)",
|
||||||
|
"DOUBLE_LAYOUT": "Use double layout",
|
||||||
|
"NEW_LINE_SEPARATOR": "Newline as split separator",
|
||||||
|
"CUSTOM_SEPARATOR": "Custom split separator"}
|
||||||
|
|
||||||
|
# Fonts that can be selected
|
||||||
|
AVAILABLE_FONTS = ("arial",
|
||||||
|
"courier new",
|
||||||
|
"fixedsys",
|
||||||
|
"ms sans serif",
|
||||||
|
"ms serif",
|
||||||
|
"system",
|
||||||
|
"times new roman",
|
||||||
|
"verdana")
|
||||||
+22
-18
@@ -5,19 +5,21 @@ Conversation with livesplit is done through the server component.
|
|||||||
import socket
|
import socket
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import config
|
import config
|
||||||
import select #used for checking if socket has data pending
|
import select # used for checking if socket has data pending
|
||||||
|
|
||||||
def ls_connect(ls_socket, call_func, window):
|
|
||||||
|
def ls_connect(ls_socket, call_func, window, server_port):
|
||||||
"""Connects given socket to the livesplit server."""
|
"""Connects given socket to the livesplit server."""
|
||||||
con_thread = Thread(target=try_connection, args=(ls_socket, call_func, window))
|
con_thread = Thread(target=try_connection, args=(ls_socket, call_func, window, server_port))
|
||||||
con_thread.start()
|
con_thread.start()
|
||||||
|
|
||||||
|
|
||||||
def init_socket():
|
def init_socket():
|
||||||
"""Returns a fresh socket"""
|
"""Returns a fresh socket"""
|
||||||
return socket.socket()
|
return socket.socket()
|
||||||
|
|
||||||
def try_connection(ls_socket, call_func, window):
|
|
||||||
|
def try_connection(ls_socket, call_func, window, server_port):
|
||||||
"""
|
"""
|
||||||
Tries to connect given socket to ls.
|
Tries to connect given socket to ls.
|
||||||
If connection is successful given "call_func"
|
If connection is successful given "call_func"
|
||||||
@@ -25,16 +27,18 @@ def try_connection(ls_socket, call_func, window):
|
|||||||
(made to be ran in a separate thread)
|
(made to be ran in a separate thread)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
ls_socket.connect((config.HOST, config.PORT))
|
ls_socket.connect((config.HOST, server_port))
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
call_func(window)
|
call_func(window)
|
||||||
|
|
||||||
|
|
||||||
def close_socket(com_socket):
|
def close_socket(com_socket):
|
||||||
"""Closes given socket."""
|
"""Closes given socket."""
|
||||||
com_socket.close()
|
com_socket.close()
|
||||||
|
|
||||||
|
|
||||||
def check_connection(ls_socket):
|
def check_connection(ls_socket):
|
||||||
"""
|
"""
|
||||||
Check so connection between socket and livesplit
|
Check so connection between socket and livesplit
|
||||||
@@ -45,8 +49,8 @@ def check_connection(ls_socket):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def send_to_ls(ls_socket, command):
|
def send_to_ls(ls_socket, command):
|
||||||
"""
|
"""
|
||||||
Sends given command to ls using given socket.
|
Sends given command to ls using given socket.
|
||||||
@@ -59,7 +63,7 @@ def send_to_ls(ls_socket, command):
|
|||||||
ls_socket.send(str.encode(config.LS_COMMANDS[command]))
|
ls_socket.send(str.encode(config.LS_COMMANDS[command]))
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
socket_ready = select.select([ls_socket], [], [], config.COM_TIMEOUT)
|
socket_ready = select.select([ls_socket], [], [], config.COM_TIMEOUT)
|
||||||
if socket_ready[0]:
|
if socket_ready[0]:
|
||||||
try:
|
try:
|
||||||
@@ -68,7 +72,7 @@ def send_to_ls(ls_socket, command):
|
|||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_split_index(ls_socket):
|
def get_split_index(ls_socket):
|
||||||
"""
|
"""
|
||||||
@@ -79,12 +83,12 @@ def get_split_index(ls_socket):
|
|||||||
Returns False on Error
|
Returns False on Error
|
||||||
"""
|
"""
|
||||||
ls_data = send_to_ls(ls_socket, "cur_split_index")
|
ls_data = send_to_ls(ls_socket, "cur_split_index")
|
||||||
|
|
||||||
if not isinstance(ls_data, bool):
|
if not isinstance(ls_data, bool):
|
||||||
return int(ls_data)
|
return int(ls_data)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_split_name(ls_socket):
|
def get_split_name(ls_socket):
|
||||||
"""
|
"""
|
||||||
@@ -92,8 +96,8 @@ def get_split_name(ls_socket):
|
|||||||
Returns False if no split is active or Error occurs.
|
Returns False if no split is active or Error occurs.
|
||||||
"""
|
"""
|
||||||
ls_data = send_to_ls(ls_socket, "cur_split_name")
|
ls_data = send_to_ls(ls_socket, "cur_split_name")
|
||||||
|
|
||||||
if ls_data:
|
if ls_data:
|
||||||
return ls_data
|
return ls_data
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|||||||
+270
-197
@@ -1,114 +1,119 @@
|
|||||||
import tkinter
|
import tkinter
|
||||||
from tkinter import messagebox
|
from tkinter import messagebox
|
||||||
from tkinter import font
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import config
|
import config
|
||||||
import ls_connection as con
|
import ls_connection as con
|
||||||
import note_reader as noter
|
import note_reader as noter
|
||||||
|
import setting_handler
|
||||||
|
|
||||||
runtime_info = {
|
runtime_info = {
|
||||||
"ls_connected": False,
|
"ls_connected": False,
|
||||||
"timer_running": False,
|
"timer_running": False,
|
||||||
"icon_active": False,
|
"active_split": -1,
|
||||||
"active_split": -1,
|
"notes": [],
|
||||||
"notes": [],
|
"server_port": 0,
|
||||||
"double_layout": False,
|
"force_reset": False,
|
||||||
"big_font": False
|
"double_layout": False,
|
||||||
}
|
"settings": {}
|
||||||
|
}
|
||||||
|
|
||||||
root = tkinter.Tk()
|
root = tkinter.Tk()
|
||||||
|
|
||||||
red_path= os.path.join(
|
red_path = os.path.join(
|
||||||
str(os.path.dirname(os.path.realpath(sys.argv[0]))),
|
str(os.path.dirname(os.path.realpath(sys.argv[0]))),
|
||||||
config.ICON_FOLDER,
|
config.RESOURCE_FOLDER,
|
||||||
config.ICONS["RED"]
|
config.ICONS["RED"]
|
||||||
)
|
)
|
||||||
green_path= os.path.join(
|
green_path = os.path.join(
|
||||||
str(os.path.dirname(os.path.realpath(sys.argv[0]))),
|
str(os.path.dirname(os.path.realpath(sys.argv[0]))),
|
||||||
config.ICON_FOLDER,
|
config.RESOURCE_FOLDER,
|
||||||
config.ICONS["GREEN"]
|
config.ICONS["GREEN"]
|
||||||
)
|
)
|
||||||
red_icon = tkinter.Image("photo", file=red_path)
|
red_icon = tkinter.Image("photo", file=red_path)
|
||||||
green_icon = tkinter.Image("photo", file=green_path)
|
green_icon = tkinter.Image("photo", file=green_path)
|
||||||
|
|
||||||
|
|
||||||
def update(window, com_socket, text1, text2):
|
def update(window, com_socket, text1, text2):
|
||||||
"""
|
"""
|
||||||
Function to loop along tkinter mainloop.
|
Function to loop along tkinter mainloop.
|
||||||
"""
|
"""
|
||||||
if not runtime_info["ls_connected"]:
|
if runtime_info["force_reset"]:
|
||||||
#try connecting to ls
|
# Boolean flag to force a connection reset
|
||||||
con.ls_connect(com_socket, server_found, window)
|
com_socket = reset_connection(com_socket, window, text1, text2)
|
||||||
|
runtime_info["force_reset"] = False
|
||||||
|
|
||||||
|
elif not runtime_info["ls_connected"]:
|
||||||
|
# try connecting to ls
|
||||||
|
con.ls_connect(com_socket, server_found, window, runtime_info["server_port"])
|
||||||
else:
|
else:
|
||||||
#is_connected
|
# is_connected
|
||||||
if runtime_info["notes"]:
|
if runtime_info["notes"]:
|
||||||
#notes loaded
|
# notes loaded
|
||||||
|
|
||||||
#get index of current split
|
# get index of current split
|
||||||
new_index = con.get_split_index(com_socket)
|
new_index = con.get_split_index(com_socket)
|
||||||
|
|
||||||
if isinstance(new_index, bool):
|
if isinstance(new_index, bool):
|
||||||
#Connection error
|
# Connection error
|
||||||
com_socket = test_connection(com_socket, window, text1, text2)
|
com_socket = test_connection(com_socket, window, text1, text2)
|
||||||
else:
|
else:
|
||||||
#index retrieved succesfully
|
# index retrieved succesfully
|
||||||
if new_index == -1:
|
if new_index == -1:
|
||||||
#timer not running
|
# timer not running
|
||||||
if runtime_info["timer_running"]:
|
if runtime_info["timer_running"]:
|
||||||
runtime_info["timer_running"] = False
|
runtime_info["timer_running"] = False
|
||||||
runtime_info["active_split"] = new_index
|
runtime_info["active_split"] = new_index
|
||||||
update_GUI(window, com_socket, text1, text2)
|
update_GUI(window, com_socket, text1, text2)
|
||||||
else:
|
else:
|
||||||
#timer is running
|
# timer is running
|
||||||
if not runtime_info["timer_running"]:
|
if not runtime_info["timer_running"]:
|
||||||
runtime_info["timer_running"] = True
|
runtime_info["timer_running"] = True
|
||||||
|
|
||||||
#special case to fix scrolling
|
# special case to fix scrolling
|
||||||
if runtime_info["active_split"] == 0:
|
if runtime_info["active_split"] == 0:
|
||||||
runtime_info["active_split"] = -1
|
runtime_info["active_split"] = -1
|
||||||
|
|
||||||
if not runtime_info["active_split"] == new_index:
|
if not runtime_info["active_split"] == new_index:
|
||||||
#new split, need to update
|
# new split, need to update
|
||||||
runtime_info["active_split"] = new_index
|
runtime_info["active_split"] = new_index
|
||||||
|
|
||||||
update_GUI(window, com_socket, text1, text2)
|
update_GUI(window, com_socket, text1, text2)
|
||||||
else:
|
else:
|
||||||
#notes not yet loaded
|
# notes not yet loaded
|
||||||
com_socket = test_connection(com_socket, window, text1, text2)
|
com_socket = test_connection(com_socket, window, text1, text2)
|
||||||
|
|
||||||
|
# self looping
|
||||||
|
window.after(int(config.POLLING_TIME * 1000),
|
||||||
#self looping
|
update, window, com_socket, text1, text2)
|
||||||
window.after(int(config.POLLING_TIME * 1000), update, window, com_socket, text1, text2)
|
|
||||||
|
|
||||||
|
|
||||||
def update_GUI(window, com_socket, text1, text2):
|
def update_GUI(window, com_socket, text1, text2):
|
||||||
"""
|
"""
|
||||||
Updates all graphics according to current runtime_info.
|
Updates all graphics according to current runtime_info.
|
||||||
Sets window title and Text-box content.
|
Sets window title and Text-box content.
|
||||||
Does NOT set window icon.
|
Does NOT set window icon.
|
||||||
"""
|
"""
|
||||||
index = runtime_info["active_split"]
|
index = runtime_info["active_split"]
|
||||||
|
|
||||||
if index == -1:
|
if index == -1:
|
||||||
index = 0
|
index = 0
|
||||||
|
|
||||||
if runtime_info["timer_running"]:
|
if runtime_info["timer_running"]:
|
||||||
#Does not test connection if it fails
|
# Does not test connection if it fails
|
||||||
split_name = con.get_split_name(com_socket)
|
split_name = con.get_split_name(com_socket)
|
||||||
else:
|
else:
|
||||||
split_name = False
|
split_name = False
|
||||||
|
|
||||||
if runtime_info["notes"]:
|
if runtime_info["notes"]:
|
||||||
set_title_notes(window, index, split_name)
|
set_title_notes(window, index, split_name)
|
||||||
update_notes(window, text1, text2, index)
|
update_notes(text1, text2, index)
|
||||||
else:
|
else:
|
||||||
update_title(config.DEFAULT_WINDOW["TITLE"], window)
|
update_title(config.DEFAULT_WINDOW["TITLE"], window)
|
||||||
|
|
||||||
|
|
||||||
def test_connection(com_socket, window, text1, text2):
|
def test_connection(com_socket, window, text1, text2):
|
||||||
"""
|
"""
|
||||||
Runs a connection test to ls using given socket.
|
Runs a connection test to ls using given socket.
|
||||||
@@ -130,16 +135,17 @@ def reset_connection(com_socket, window, text1, text2):
|
|||||||
if runtime_info["timer_running"]:
|
if runtime_info["timer_running"]:
|
||||||
runtime_info["timer_running"] = False
|
runtime_info["timer_running"] = False
|
||||||
runtime_info["active_split"] = -1
|
runtime_info["active_split"] = -1
|
||||||
|
|
||||||
runtime_info["ls_connected"] = False
|
runtime_info["ls_connected"] = False
|
||||||
|
|
||||||
update_icon(False, window)
|
update_icon(False, window)
|
||||||
update_GUI(window, com_socket, text1, text2)
|
update_GUI(window, com_socket, text1, text2)
|
||||||
|
|
||||||
#Close old and return a fresh socket
|
# Close old and return a fresh socket
|
||||||
con.close_socket(com_socket)
|
con.close_socket(com_socket)
|
||||||
|
|
||||||
return con.init_socket()
|
return con.init_socket()
|
||||||
|
|
||||||
|
|
||||||
def server_found(window):
|
def server_found(window):
|
||||||
"""
|
"""
|
||||||
@@ -149,22 +155,20 @@ def server_found(window):
|
|||||||
runtime_info["ls_connected"] = True
|
runtime_info["ls_connected"] = True
|
||||||
update_icon(True, window)
|
update_icon(True, window)
|
||||||
|
|
||||||
|
|
||||||
def update_icon(active, window):
|
def update_icon(active, window):
|
||||||
"""Updates icon of window depending on "active" variable"""
|
"""Updates icon of window depending on "active" variable"""
|
||||||
if active and not runtime_info["icon_active"]:
|
if active:
|
||||||
window.tk.call('wm', 'iconphoto', window._w, green_icon)
|
window.tk.call('wm', 'iconphoto', window._w, green_icon)
|
||||||
runtime_info["icon_active"] = True
|
else:
|
||||||
elif runtime_info["icon_active"]:
|
|
||||||
window.tk.call('wm', 'iconphoto', window._w, red_icon)
|
window.tk.call('wm', 'iconphoto', window._w, red_icon)
|
||||||
runtime_info["icon_active"] = False
|
|
||||||
|
|
||||||
|
|
||||||
def update_title(name, window):
|
def update_title(name, window):
|
||||||
"""Sets the title of given window to name."""
|
"""Sets the title of given window to name."""
|
||||||
window.wm_title(name)
|
window.wm_title(name)
|
||||||
|
|
||||||
|
|
||||||
def adjust_content(window, box1, box2):
|
def adjust_content(window, box1, box2):
|
||||||
"""
|
"""
|
||||||
Adjusts size of box1 and box2 according to
|
Adjusts size of box1 and box2 according to
|
||||||
@@ -174,87 +178,83 @@ def adjust_content(window, box1, box2):
|
|||||||
set_double_layout(window, box1, box2)
|
set_double_layout(window, box1, box2)
|
||||||
else:
|
else:
|
||||||
set_single_layout(window, box1, box2)
|
set_single_layout(window, box1, box2)
|
||||||
|
|
||||||
|
|
||||||
def set_double_layout(window, box1, box2):
|
def set_double_layout(window, box1, box2):
|
||||||
"""
|
"""
|
||||||
Configures boxes in the window to fit as in double layout.
|
Configures boxes in the window to fit as in double layout.
|
||||||
"""
|
"""
|
||||||
runtime_info["double_layout"] = True
|
runtime_info["double_layout"] = True
|
||||||
|
|
||||||
w_width = window.winfo_width()
|
w_width = window.winfo_width()
|
||||||
w_height = window.winfo_height()
|
w_height = window.winfo_height()
|
||||||
|
|
||||||
box1.place(height=(w_height // 2), width=w_width)
|
box1.place(height=(w_height // 2), width=w_width)
|
||||||
box2.place(height=(w_height // 2), width=w_width, y=(w_height // 2))
|
box2.place(height=(w_height // 2), width=w_width, y=(w_height // 2))
|
||||||
|
|
||||||
|
|
||||||
def set_single_layout(window, box1, box2):
|
def set_single_layout(window, box1, box2):
|
||||||
"""
|
"""
|
||||||
Configures boxes in the window to fit as in single layout.
|
Configures boxes in the window to fit as in single layout.
|
||||||
"""
|
"""
|
||||||
runtime_info["double_layout"] = False
|
runtime_info["double_layout"] = False
|
||||||
|
|
||||||
box2.place_forget()
|
box2.place_forget()
|
||||||
box1.place(height=window.winfo_height(), width=window.winfo_width())
|
box1.place(height=window.winfo_height(), width=window.winfo_width())
|
||||||
|
|
||||||
|
|
||||||
def show_popup(event, menu):
|
def show_popup(event, menu):
|
||||||
"""Displays given popup menu at cursor position."""
|
"""Displays given popup menu at cursor position."""
|
||||||
menu.post(event.x_root, event.y_root)
|
menu.post(event.x_root, event.y_root)
|
||||||
|
|
||||||
|
|
||||||
def menu_change_layout(window, box1, box2, popup):
|
|
||||||
"""Menu option for changing layout selected."""
|
|
||||||
if runtime_info["double_layout"]:
|
|
||||||
set_single_layout(window, box1, box2)
|
|
||||||
popup.entryconfig(0, label=config.MENU_OPTIONS["DOUBLE"])
|
|
||||||
else:
|
|
||||||
set_double_layout(window, box1, box2)
|
|
||||||
popup.entryconfig(0, label=config.MENU_OPTIONS["SINGLE"])
|
|
||||||
|
|
||||||
|
|
||||||
def menu_load_notes(window, text1, text2, com_socket):
|
def menu_load_notes(window, text1, text2, com_socket):
|
||||||
"""Menu selected load notes option."""
|
"""Menu selected load notes option."""
|
||||||
load_notes(window, text1, text2, com_socket)
|
load_notes(window, text1, text2, com_socket)
|
||||||
|
|
||||||
|
|
||||||
def load_notes(window, text1, text2, com_socket):
|
def load_notes(window, text1, text2, com_socket):
|
||||||
"""
|
"""
|
||||||
Prompts user to select notes and then tries to load these into the UI.
|
Prompts user to select notes and then tries to load these into the UI.
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
|
||||||
|
# Save notes to settings
|
||||||
|
settings = setting_handler.load_settings()
|
||||||
|
settings["notes"] = file
|
||||||
|
setting_handler.save_settings(settings)
|
||||||
|
|
||||||
split_c = len(notes)
|
split_c = len(notes)
|
||||||
show_info(("Notes Loaded", ("Loaded notes with " + str(split_c) + " splits.")))
|
show_info(("Notes Loaded",
|
||||||
|
("Loaded notes with " + str(split_c) + " splits.")))
|
||||||
|
|
||||||
if not runtime_info["timer_running"]:
|
if not runtime_info["timer_running"]:
|
||||||
runtime_info["active_split"] = -1
|
runtime_info["active_split"] = -1
|
||||||
|
|
||||||
update_GUI(window, com_socket, text1, text2)
|
update_GUI(window, com_socket, text1, text2)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
show_info(config.ERRORS["NOTES_EMPTY"], True)
|
show_info(config.ERRORS["NOTES_EMPTY"], True)
|
||||||
|
|
||||||
|
|
||||||
def show_info(info, warning = False):
|
def show_info(info, warning=False):
|
||||||
"""
|
"""
|
||||||
Displays an infor popup window.
|
Displays an info popup window.
|
||||||
if warning is True window has a warning triangle.
|
if warning is True window has a warning triangle.
|
||||||
"""
|
"""
|
||||||
if warning:
|
if warning:
|
||||||
messagebox.showwarning(info[0], info[1])
|
messagebox.showwarning(info[0], info[1])
|
||||||
else:
|
else:
|
||||||
messagebox.showinfo(info[0], info[1])
|
messagebox.showinfo(info[0], info[1])
|
||||||
|
|
||||||
|
|
||||||
def update_notes(window, text1, text2, index):
|
def update_notes(text1, text2, index):
|
||||||
"""
|
"""
|
||||||
Displays notes with the given index in given text widgets.
|
Displays notes with the given index in given text widgets.
|
||||||
If index is lower than 0, displays notes for index 0.
|
If index is lower than 0, displays notes for index 0.
|
||||||
@@ -264,179 +264,252 @@ def update_notes(window, text1, text2, index):
|
|||||||
text2 is always given the notes at index (index + 1) if existing
|
text2 is always given the notes at index (index + 1) if existing
|
||||||
"""
|
"""
|
||||||
max_index = (len(runtime_info["notes"]) - 1)
|
max_index = (len(runtime_info["notes"]) - 1)
|
||||||
|
|
||||||
if index < 0:
|
if index < 0:
|
||||||
index = 0
|
index = 0
|
||||||
|
|
||||||
text1.config(state=tkinter.NORMAL)
|
text1.config(state=tkinter.NORMAL)
|
||||||
text2.config(state=tkinter.NORMAL)
|
text2.config(state=tkinter.NORMAL)
|
||||||
|
|
||||||
text1.delete("1.0", tkinter.END)
|
text1.delete("1.0", tkinter.END)
|
||||||
text2.delete("1.0", tkinter.END)
|
text2.delete("1.0", tkinter.END)
|
||||||
|
|
||||||
if index <= max_index:
|
if index <= max_index:
|
||||||
text1.insert(tkinter.END, runtime_info["notes"][index])
|
text1.insert(tkinter.END, runtime_info["notes"][index])
|
||||||
|
|
||||||
#cand disply notes for index+1
|
# cant disply notes for index+1
|
||||||
if index < max_index:
|
if index < max_index:
|
||||||
text2.insert(tkinter.END, runtime_info["notes"][index + 1])
|
text2.insert(tkinter.END, runtime_info["notes"][index + 1])
|
||||||
|
|
||||||
text1.config(state=tkinter.DISABLED)
|
text1.config(state=tkinter.DISABLED)
|
||||||
text2.config(state=tkinter.DISABLED)
|
text2.config(state=tkinter.DISABLED)
|
||||||
|
|
||||||
|
|
||||||
def right_arrow(window, com_socket, text1, text2):
|
def right_arrow(window, com_socket, text1, text2):
|
||||||
"""Event handler for right arrow key."""
|
"""Event handler for right arrow key."""
|
||||||
change_preview(window, com_socket, text1, text2, 1)
|
change_preview(window, com_socket, text1, text2, 1)
|
||||||
|
|
||||||
|
|
||||||
def left_arrow(window, com_socket, text1, text2):
|
def left_arrow(window, com_socket, text1, text2):
|
||||||
"""Event handler for left arrow key."""
|
"""Event handler for left arrow key."""
|
||||||
change_preview(window, com_socket, text1, text2, -1)
|
change_preview(window, com_socket, text1, text2, -1)
|
||||||
|
|
||||||
|
|
||||||
def change_preview(window, com_socket, text1, text2, move):
|
def change_preview(window, com_socket, text1, text2, move):
|
||||||
"""move is either 1 for next or -1 for previous."""
|
"""
|
||||||
|
Chnges notes that are currently displayed.
|
||||||
|
Move is either 1 for next or -1 for previous.
|
||||||
|
"""
|
||||||
if runtime_info["notes"] and (not runtime_info["timer_running"]):
|
if runtime_info["notes"] and (not runtime_info["timer_running"]):
|
||||||
max_index = (len(runtime_info["notes"]) - 1)
|
max_index = (len(runtime_info["notes"]) - 1)
|
||||||
index = runtime_info["active_split"]
|
index = runtime_info["active_split"]
|
||||||
|
|
||||||
if index < 0:
|
if index < 0:
|
||||||
index = 0
|
index = 0
|
||||||
|
|
||||||
index += move
|
index += move
|
||||||
|
|
||||||
if index > max_index:
|
if index > max_index:
|
||||||
index = max_index
|
index = max_index
|
||||||
|
|
||||||
runtime_info["active_split"] = index
|
runtime_info["active_split"] = index
|
||||||
|
|
||||||
update_GUI(window, com_socket, text1, text2)
|
update_GUI(window, com_socket, text1, text2)
|
||||||
|
|
||||||
def set_title_notes(window, index, split_name = False):
|
|
||||||
|
def set_title_notes(window, index, split_name=False):
|
||||||
"""
|
"""
|
||||||
Set window title to fit with displayed notes.
|
Set window title to fit with displayed notes.
|
||||||
"""
|
"""
|
||||||
title = config.DEFAULT_WINDOW["TITLE"]
|
title = config.DEFAULT_WINDOW["TITLE"]
|
||||||
|
|
||||||
disp_index = str(index + 1) #start at 1
|
disp_index = str(index + 1) # start at 1
|
||||||
title += " - " + disp_index
|
title += " - " + disp_index
|
||||||
|
|
||||||
if split_name:
|
if split_name:
|
||||||
title += " - " + split_name
|
title += " - " + split_name
|
||||||
|
|
||||||
if runtime_info["timer_running"]:
|
if runtime_info["timer_running"]:
|
||||||
title += " - " + config.RUNNING_ALERT
|
title += " - " + config.RUNNING_ALERT
|
||||||
|
|
||||||
update_title(title, window)
|
update_title(title, window)
|
||||||
|
|
||||||
|
|
||||||
def menu_font_size(text_font, menu):
|
def menu_open_settings(root_wnd, box1, box2, text1, text2, com_socket):
|
||||||
if runtime_info["big_font"]:
|
"""
|
||||||
runtime_info["big_font"] = False
|
Opens the settings menu.
|
||||||
menu.entryconfig(1, label=config.MENU_OPTIONS["BIG"])
|
"""
|
||||||
|
setting_handler.edit_settings(root_wnd,
|
||||||
|
(lambda settings: apply_settings(settings,
|
||||||
|
root_wnd,
|
||||||
|
box1, box2,
|
||||||
|
text1, text2, com_socket)))
|
||||||
|
|
||||||
|
|
||||||
|
def apply_settings(settings, window, box1, box2, text1, text2, com_socket):
|
||||||
|
"""
|
||||||
|
Applies the given settings to the given components.
|
||||||
|
Settings must be a correctly formatted dictionary.
|
||||||
|
"""
|
||||||
|
runtime_info["settings"] = settings
|
||||||
|
|
||||||
|
# Server port change
|
||||||
|
if not (runtime_info["server_port"] == int(settings["server_port"])):
|
||||||
|
runtime_info["server_port"] = int(settings["server_port"])
|
||||||
|
runtime_info["force_reset"] = True
|
||||||
|
|
||||||
|
text_font = (settings["font"], int(settings["font_size"]))
|
||||||
|
|
||||||
|
if setting_handler.decode_boolean_setting(settings["double_layout"]):
|
||||||
|
set_double_layout(window, box1, box2)
|
||||||
else:
|
else:
|
||||||
runtime_info["big_font"] = True
|
set_single_layout(window, box1, box2)
|
||||||
menu.entryconfig(1, label=config.MENU_OPTIONS["SMALL"])
|
|
||||||
|
text1.config(font=text_font)
|
||||||
update_font_size(text_font)
|
text2.config(font=text_font)
|
||||||
|
text1.config(fg=settings["text_color"], bg=settings["background_color"])
|
||||||
def update_font_size(text_font):
|
text2.config(fg=settings["text_color"], bg=settings["background_color"])
|
||||||
if runtime_info["big_font"]:
|
|
||||||
font_size = config.FONT["BIG"]
|
old_note_length = len(runtime_info["notes"])
|
||||||
else:
|
|
||||||
font_size = config.FONT["SMALL"]
|
if settings["notes"] and noter.file_exists(settings["notes"]):
|
||||||
|
new_notes = noter.get_notes(settings["notes"], settings["separator"])
|
||||||
text_font.config(size=font_size)
|
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
Saves given width and height to settigns file.
|
||||||
|
"""
|
||||||
|
settings = setting_handler.load_settings()
|
||||||
|
settings["width"] = str(width)
|
||||||
|
settings["height"] = str(height)
|
||||||
|
setting_handler.save_settings(settings)
|
||||||
|
|
||||||
|
|
||||||
|
def do_on_close(root_wnd):
|
||||||
|
"""
|
||||||
|
Function that is called when the main tk window is closed.
|
||||||
|
Saves root_wnd's width and height to the settings file and
|
||||||
|
then closes the window.
|
||||||
|
"""
|
||||||
|
save_geometry_settings(root_wnd.winfo_width(), root_wnd.winfo_height())
|
||||||
|
root_wnd.destroy()
|
||||||
|
|
||||||
|
|
||||||
def init_UI(root):
|
def init_UI(root):
|
||||||
"""Draws default UI and creates event bindings."""
|
"""Draws default UI and creates event bindings."""
|
||||||
|
|
||||||
#Create communication socket
|
# Create communication socket
|
||||||
com_socket = con.init_socket()
|
com_socket = con.init_socket()
|
||||||
|
|
||||||
#Graphical components
|
# Load Settings
|
||||||
root.geometry(str(config.DEFAULT_WINDOW["WIDTH"]) + "x" + str(config.DEFAULT_WINDOW["HEIGHT"]))
|
settings = setting_handler.load_settings()
|
||||||
|
runtime_info["server_port"] = int(settings["server_port"])
|
||||||
|
runtime_info["settings"] = settings
|
||||||
|
|
||||||
|
# Graphical components
|
||||||
|
root.geometry(settings["width"] + "x" + settings["height"])
|
||||||
|
|
||||||
box1 = tkinter.Frame(root)
|
box1 = tkinter.Frame(root)
|
||||||
box2 = tkinter.Frame(root)
|
box2 = tkinter.Frame(root)
|
||||||
|
|
||||||
scroll1 = tkinter.Scrollbar(box1)
|
scroll1 = tkinter.Scrollbar(box1, width=config.SCROLLBAR_WIDTH)
|
||||||
|
scroll2 = tkinter.Scrollbar(box2, width=config.SCROLLBAR_WIDTH)
|
||||||
scroll1.pack(side=tkinter.RIGHT, fill=tkinter.Y)
|
scroll1.pack(side=tkinter.RIGHT, fill=tkinter.Y)
|
||||||
|
|
||||||
scroll2 = tkinter.Scrollbar(box2)
|
|
||||||
scroll2.pack(side=tkinter.RIGHT, fill=tkinter.Y)
|
scroll2.pack(side=tkinter.RIGHT, fill=tkinter.Y)
|
||||||
|
|
||||||
|
|
||||||
text1 = tkinter.Text(
|
text1 = tkinter.Text(
|
||||||
box1,
|
box1,
|
||||||
yscrollcommand=scroll1.set,
|
yscrollcommand=scroll1.set,
|
||||||
wrap=tkinter.WORD,
|
wrap=tkinter.WORD,
|
||||||
cursor="arrow"
|
cursor="arrow"
|
||||||
)
|
)
|
||||||
text1.insert(tkinter.END, config.DEFAULT_MSG)
|
text1.insert(tkinter.END, config.DEFAULT_MSG)
|
||||||
text1.config(state=tkinter.DISABLED)
|
text1.config(state=tkinter.DISABLED)
|
||||||
text1.pack(fill=tkinter.BOTH, expand=True)
|
text1.pack(fill=tkinter.BOTH, expand=True)
|
||||||
|
|
||||||
text2 = tkinter.Text(
|
text2 = tkinter.Text(
|
||||||
box2,
|
box2,
|
||||||
yscrollcommand=scroll2.set,
|
yscrollcommand=scroll2.set,
|
||||||
wrap=tkinter.WORD,
|
wrap=tkinter.WORD,
|
||||||
cursor="arrow"
|
cursor="arrow"
|
||||||
)
|
)
|
||||||
text2.insert(tkinter.END, config.DEFAULT_MSG)
|
text2.insert(tkinter.END, config.DEFAULT_MSG)
|
||||||
text2.config(state=tkinter.DISABLED)
|
text2.config(state=tkinter.DISABLED)
|
||||||
text2.pack(fill=tkinter.BOTH, expand=True)
|
text2.pack(fill=tkinter.BOTH, expand=True)
|
||||||
|
|
||||||
scroll1.config(command=text1.yview)
|
scroll1.config(command=text1.yview)
|
||||||
scroll2.config(command=text2.yview)
|
scroll2.config(command=text2.yview)
|
||||||
|
|
||||||
#Set font and color for text
|
# Set font and color for text
|
||||||
text_font = font.Font(
|
text_font = (settings["font"], int(settings["font_size"]))
|
||||||
family=config.FONT["NAME"],
|
|
||||||
size=config.FONT["SMALL"]
|
|
||||||
)
|
|
||||||
text1.config(font=text_font)
|
text1.config(font=text_font)
|
||||||
text2.config(font=text_font)
|
text2.config(font=text_font)
|
||||||
text1.config(fg=config.COLOR["TEXT"], bg=config.COLOR["TEXT_BG"])
|
text1.config(fg=settings["text_color"], bg=settings["background_color"])
|
||||||
text2.config(fg=config.COLOR["TEXT"], bg=config.COLOR["TEXT_BG"])
|
text2.config(fg=settings["text_color"], bg=settings["background_color"])
|
||||||
|
|
||||||
set_single_layout(root, box1, box2)
|
if setting_handler.decode_boolean_setting(settings["double_layout"]):
|
||||||
|
set_double_layout(root, box1, box2)
|
||||||
#create popup menu
|
else:
|
||||||
|
set_single_layout(root, box1, box2)
|
||||||
|
|
||||||
|
# create popup menu
|
||||||
popup = tkinter.Menu(root, tearoff=0)
|
popup = tkinter.Menu(root, tearoff=0)
|
||||||
popup.add_command(
|
popup.add_command(
|
||||||
label=config.MENU_OPTIONS["DOUBLE"],
|
label=config.MENU_OPTIONS["LOAD"],
|
||||||
command=(
|
command=(lambda: menu_load_notes(root, text1, text2, com_socket))
|
||||||
lambda: menu_change_layout(root, box1, box2, popup)
|
)
|
||||||
)
|
|
||||||
) #Needs to be at index 0
|
|
||||||
popup.add_command(
|
popup.add_command(
|
||||||
label=config.MENU_OPTIONS["BIG"],
|
label=config.MENU_OPTIONS["SETTINGS"],
|
||||||
command=(
|
command=(lambda: menu_open_settings(root, box1, box2, text1, text2, com_socket))
|
||||||
lambda: menu_font_size(text_font, popup)
|
)
|
||||||
)
|
|
||||||
) #Needs to be at index 1
|
# Set default window icon and title
|
||||||
popup.add_command(
|
|
||||||
label=config.MENU_OPTIONS["LOAD"],
|
|
||||||
command=(lambda: menu_load_notes(root, text1, text2, com_socket))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#Set default window icon and title
|
|
||||||
root.tk.call('wm', 'iconphoto', root._w, red_icon)
|
root.tk.call('wm', 'iconphoto', root._w, red_icon)
|
||||||
update_title(config.DEFAULT_WINDOW["TITLE"], root)
|
update_title(config.DEFAULT_WINDOW["TITLE"], root)
|
||||||
|
|
||||||
#Event binds
|
# Check if notes can be loaded from settings
|
||||||
|
settings = setting_handler.load_settings()
|
||||||
|
|
||||||
|
if settings["notes"] and noter.file_exists(settings["notes"]):
|
||||||
|
notes = noter.get_notes(settings["notes"], settings["separator"])
|
||||||
|
|
||||||
|
if notes:
|
||||||
|
runtime_info["notes"] = notes
|
||||||
|
update_GUI(root, com_socket, text1, text2)
|
||||||
|
|
||||||
|
# Event binds
|
||||||
root.bind("<Configure>", (lambda e: adjust_content(root, box1, box2)))
|
root.bind("<Configure>", (lambda e: adjust_content(root, box1, box2)))
|
||||||
root.bind("<Button-3>", (lambda e: show_popup(e, popup)))
|
root.bind("<Button-3>", (lambda e: show_popup(e, popup)))
|
||||||
root.bind("<Right>", (lambda e: right_arrow(root, com_socket, text1, text2)))
|
root.bind("<Right>", (lambda e: right_arrow(root, com_socket, text1, text2)))
|
||||||
root.bind("<Left>", (lambda e: left_arrow(root, com_socket, text1, text2)))
|
root.bind("<Left>", (lambda e: left_arrow(root, com_socket, text1, text2)))
|
||||||
|
|
||||||
#call update loop
|
# Window close bind
|
||||||
|
root.protocol("WM_DELETE_WINDOW", (lambda: do_on_close(root)))
|
||||||
|
|
||||||
|
# call update loop
|
||||||
update(root, com_socket, text1, text2)
|
update(root, com_socket, text1, text2)
|
||||||
|
|
||||||
|
root.geometry(settings["width"] + "x" + settings["height"])
|
||||||
|
|
||||||
init_UI(root)
|
init_UI(root)
|
||||||
|
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|||||||
+49
-33
@@ -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)
|
||||||
@@ -15,15 +16,15 @@ these can be used for titles.
|
|||||||
all other lines of text are added as notes
|
all other lines of text are added as notes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_note_lines(file_path):
|
def get_note_lines(file_path):
|
||||||
"""
|
"""
|
||||||
Reads file at given path and returns
|
Reads file at given path and returns
|
||||||
a list containing all rows of text in given file.
|
a list containing all rows of text in given file.
|
||||||
Returns false if file can not be read.
|
Returns false if file can not be read.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
#check so file isn't too big
|
# check so file isn't too big
|
||||||
if path.getsize(file_path) > config.MAX_FILE_SIZE:
|
if path.getsize(file_path) > config.MAX_FILE_SIZE:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -31,37 +32,49 @@ def get_note_lines(file_path):
|
|||||||
notes_file = open(file_path, "r")
|
notes_file = open(file_path, "r")
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#read file line per line
|
# read file line per line
|
||||||
f_lines = []
|
f_lines = []
|
||||||
keep_reading = True
|
keep_reading = True
|
||||||
while keep_reading:
|
while keep_reading:
|
||||||
cur_line = notes_file.readline()
|
|
||||||
|
try:
|
||||||
|
cur_line = notes_file.readline()
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
if cur_line:
|
if cur_line:
|
||||||
f_lines.append(cur_line)
|
f_lines.append(cur_line)
|
||||||
else:
|
else:
|
||||||
keep_reading = False
|
keep_reading = False
|
||||||
|
|
||||||
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])):
|
||||||
return line[:-1]
|
return line[:-1]
|
||||||
@@ -70,27 +83,25 @@ def encode_notes(note_lines):
|
|||||||
|
|
||||||
note_list = []
|
note_list = []
|
||||||
cur_notes = ""
|
cur_notes = ""
|
||||||
|
|
||||||
for line in note_lines:
|
|
||||||
#remove whitespace at beginning and end
|
|
||||||
line = line.strip(" ")
|
|
||||||
|
|
||||||
if is_newline(line):
|
for line in note_lines:
|
||||||
|
line = remove_new_line(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
|
||||||
|
|
||||||
if cur_notes:
|
if cur_notes:
|
||||||
note_list.append(cur_notes)
|
note_list.append(cur_notes)
|
||||||
|
|
||||||
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.
|
||||||
@@ -98,12 +109,12 @@ def get_notes(file_path):
|
|||||||
Returns False if file is empty.
|
Returns False if file is empty.
|
||||||
"""
|
"""
|
||||||
note_lines = get_note_lines(file_path)
|
note_lines = get_note_lines(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
|
||||||
|
|
||||||
|
|
||||||
@@ -113,10 +124,15 @@ def select_file():
|
|||||||
Returns False upon no file selection.
|
Returns False upon no file selection.
|
||||||
Otherwise returns absolute path to selected file.
|
Otherwise returns absolute path to selected file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
file = file_dia.askopenfilename(filetypes=config.TEXT_FILES)
|
file = file_dia.askopenfilename(filetypes=config.TEXT_FILES)
|
||||||
|
|
||||||
if file:
|
if file:
|
||||||
return file
|
return file
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def file_exists(file):
|
||||||
|
"""Checks if given path leads to an existing file."""
|
||||||
|
return path.isfile(file)
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,425 @@
|
|||||||
|
import tkinter.colorchooser as colorchooser
|
||||||
|
from tkinter import messagebox as msgbox
|
||||||
|
import tkinter
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
settings_path = os.path.join(
|
||||||
|
str(os.path.dirname(os.path.realpath(sys.argv[0]))),
|
||||||
|
config.RESOURCE_FOLDER,
|
||||||
|
config.SETTINGS_FILE
|
||||||
|
)
|
||||||
|
|
||||||
|
settings_icon_path = os.path.join(
|
||||||
|
str(os.path.dirname(os.path.realpath(sys.argv[0]))),
|
||||||
|
config.RESOURCE_FOLDER,
|
||||||
|
config.ICONS["SETTINGS"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_settings():
|
||||||
|
"""
|
||||||
|
Tries to load settings from file.
|
||||||
|
If no working settings-file exist one is created and the default settings are returned.
|
||||||
|
|
||||||
|
returns a dictionary with all settings.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# try to open default settings file
|
||||||
|
try:
|
||||||
|
settings_file = open(settings_path, "r+")
|
||||||
|
settings_content = get_file_lines(settings_file)
|
||||||
|
except:
|
||||||
|
# File not found
|
||||||
|
settings_content = set_default_settings()
|
||||||
|
|
||||||
|
settings = format_settings(settings_content)
|
||||||
|
|
||||||
|
# Check so settings file has all settings
|
||||||
|
if not validate_settings(settings):
|
||||||
|
settings = format_settings(set_default_settings())
|
||||||
|
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
|
def set_default_settings():
|
||||||
|
"""
|
||||||
|
Creates a config file with default settings.
|
||||||
|
Returns the default config-file content.
|
||||||
|
"""
|
||||||
|
set_settings_file_content(config.DEFAULT_CONFIG)
|
||||||
|
return config.DEFAULT_CONFIG.split("\n")
|
||||||
|
|
||||||
|
|
||||||
|
def format_settings(file_rows):
|
||||||
|
"""
|
||||||
|
Takes a list of settings (as written in config files) and
|
||||||
|
formats it to a dictionary.
|
||||||
|
|
||||||
|
Returns a dictionary with all settings as keys.
|
||||||
|
"""
|
||||||
|
SETTING_PART_LENGTH = 2
|
||||||
|
|
||||||
|
settings = {}
|
||||||
|
|
||||||
|
for row in file_rows:
|
||||||
|
row = row.strip("\n")
|
||||||
|
parts = row.split("=", 1)
|
||||||
|
|
||||||
|
if len(parts) == SETTING_PART_LENGTH:
|
||||||
|
# Strip to remove whitespace at end and beginning
|
||||||
|
settings[parts[0].strip()] = parts[1].strip()
|
||||||
|
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_lines(file):
|
||||||
|
"""
|
||||||
|
Returns a list containing all the lines of the gicen file.
|
||||||
|
"""
|
||||||
|
# read file line per line
|
||||||
|
f_lines = []
|
||||||
|
keep_reading = True
|
||||||
|
while keep_reading:
|
||||||
|
cur_line = file.readline()
|
||||||
|
|
||||||
|
if cur_line:
|
||||||
|
f_lines.append(cur_line)
|
||||||
|
else:
|
||||||
|
keep_reading = False
|
||||||
|
|
||||||
|
return f_lines
|
||||||
|
|
||||||
|
|
||||||
|
def validate_settings(settings):
|
||||||
|
"""
|
||||||
|
Checks a settings dictionary so that all the needed settings are present.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for req_setting in config.REQUIRED_SETTINGS:
|
||||||
|
if not (req_setting in settings):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_font_size(settings["font_size"]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_server_port(settings["server_port"]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_color(settings["text_color"]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_color(settings["background_color"]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not (settings["font"] in config.AVAILABLE_FONTS):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not ((settings["double_layout"] == "True") or
|
||||||
|
(settings["double_layout"] == "False")):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_pixels(settings["width"]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_pixels(settings["height"]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_separator(settings["separator"]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def set_settings_file_content(content):
|
||||||
|
"""
|
||||||
|
Saves given content to the config file, config.cfg, in the resources directory.
|
||||||
|
"""
|
||||||
|
settings_file = open(settings_path, "w")
|
||||||
|
settings_file.write(content)
|
||||||
|
settings_file.close()
|
||||||
|
|
||||||
|
|
||||||
|
def edit_settings(root_wnd, apply_method):
|
||||||
|
"""
|
||||||
|
Sets up a window for editing settings.
|
||||||
|
root_wnd is the main window that settings should be applied to.
|
||||||
|
apply_method is the method to be called to apply validated settings.
|
||||||
|
"""
|
||||||
|
settings_wnd = tkinter.Toplevel(master=root_wnd,
|
||||||
|
width=config.SETTINGS_WINDOW["WIDTH"],
|
||||||
|
height=config.SETTINGS_WINDOW["HEIGHT"])
|
||||||
|
settings_wnd.title(config.SETTINGS_WINDOW["TITLE"])
|
||||||
|
settings_icon = tkinter.Image("photo", file=settings_icon_path)
|
||||||
|
settings_wnd.tk.call('wm', 'iconphoto', settings_wnd._w, settings_icon)
|
||||||
|
|
||||||
|
settings = load_settings()
|
||||||
|
|
||||||
|
settings_wnd.resizable(0, 0)
|
||||||
|
|
||||||
|
font_label = tkinter.Label(settings_wnd,
|
||||||
|
text=config.SETTINGS_OPTIONS["FONT"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
font_size_label = tkinter.Label(settings_wnd,
|
||||||
|
text=config.SETTINGS_OPTIONS["FONT_SIZE"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
text_color_label = tkinter.Label(settings_wnd,
|
||||||
|
text=config.SETTINGS_OPTIONS["TEXT_COLOR"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
bg_color_label = tkinter.Label(settings_wnd,
|
||||||
|
text=config.SETTINGS_OPTIONS["BG_COLOR"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
layout_label = tkinter.Label(settings_wnd,
|
||||||
|
text=config.SETTINGS_OPTIONS["DOUBLE_LAYOUT"],
|
||||||
|
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,
|
||||||
|
text=config.SETTINGS_OPTIONS["SERVER_PORT"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
default_port_label = tkinter.Label(settings_wnd,
|
||||||
|
text=config.SETTINGS_OPTIONS["DEFAULT_SERVER_PORT"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
|
||||||
|
# Font Selection
|
||||||
|
selected_font = tkinter.StringVar(settings_wnd)
|
||||||
|
selected_font.set(settings["font"])
|
||||||
|
|
||||||
|
font_dropdown = tkinter.OptionMenu(settings_wnd,
|
||||||
|
selected_font,
|
||||||
|
*config.AVAILABLE_FONTS)
|
||||||
|
font_dropdown.configure(font=config.GUI_FONT)
|
||||||
|
|
||||||
|
# Font Size Selection
|
||||||
|
font_size_entry = tkinter.Entry(settings_wnd, width=2, font=config.GUI_FONT)
|
||||||
|
font_size_entry.insert(0, settings["font_size"])
|
||||||
|
|
||||||
|
# Text Color Selection
|
||||||
|
text_color = tkinter.Button(settings_wnd,
|
||||||
|
width=3,
|
||||||
|
height=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if validate_color(settings["text_color"]):
|
||||||
|
text_color.configure(background=settings["text_color"])
|
||||||
|
else:
|
||||||
|
text_color.configure(background="#000000")
|
||||||
|
|
||||||
|
def text_color_selection():
|
||||||
|
choosen_color = colorchooser.askcolor()
|
||||||
|
if choosen_color[1]:
|
||||||
|
settings["text_color"] = choosen_color[1]
|
||||||
|
text_color.configure(background=settings["text_color"])
|
||||||
|
|
||||||
|
settings_wnd.focus_force()
|
||||||
|
|
||||||
|
text_color.configure(command=text_color_selection)
|
||||||
|
|
||||||
|
# Background color Selection
|
||||||
|
bg_color = tkinter.Button(settings_wnd,
|
||||||
|
width=3,
|
||||||
|
height=1)
|
||||||
|
|
||||||
|
if validate_color(settings["background_color"]):
|
||||||
|
bg_color.configure(background=settings["background_color"])
|
||||||
|
else:
|
||||||
|
bg_color.configure(background="#FFFFFF")
|
||||||
|
|
||||||
|
def bg_color_selection():
|
||||||
|
choosen_color = colorchooser.askcolor()
|
||||||
|
if choosen_color[1]:
|
||||||
|
settings["background_color"] = choosen_color[1]
|
||||||
|
bg_color.configure(background=settings["background_color"])
|
||||||
|
|
||||||
|
settings_wnd.focus_force()
|
||||||
|
|
||||||
|
bg_color.configure(command=bg_color_selection)
|
||||||
|
|
||||||
|
# Server port Selection
|
||||||
|
port_entry = tkinter.Entry(settings_wnd, width=6, font=config.GUI_FONT)
|
||||||
|
port_entry.insert(0, settings["server_port"])
|
||||||
|
|
||||||
|
# Double Layout Selection
|
||||||
|
double_layout = tkinter.BooleanVar()
|
||||||
|
double_layout_btn = tkinter.Checkbutton(settings_wnd, variable=double_layout)
|
||||||
|
|
||||||
|
if decode_boolean_setting(settings["double_layout"]):
|
||||||
|
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
|
||||||
|
def control_and_save():
|
||||||
|
errors_found = False
|
||||||
|
|
||||||
|
settings["font"] = selected_font.get()
|
||||||
|
|
||||||
|
chosen_font_size = font_size_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())
|
||||||
|
|
||||||
|
if not validate_font_size(chosen_font_size):
|
||||||
|
msgbox.showerror(config.ERRORS["FONT_SIZE"][0], config.ERRORS["FONT_SIZE"][1])
|
||||||
|
errors_found = True
|
||||||
|
else:
|
||||||
|
settings["font_size"] = chosen_font_size
|
||||||
|
|
||||||
|
if not validate_server_port(chosen_port):
|
||||||
|
msgbox.showerror(config.ERRORS["SERVER_PORT"][0], config.ERRORS["SERVER_PORT"][1])
|
||||||
|
errors_found = True
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
save_settings(settings)
|
||||||
|
apply_method(settings)
|
||||||
|
settings_wnd.destroy()
|
||||||
|
else:
|
||||||
|
settings_wnd.focus_force()
|
||||||
|
|
||||||
|
save_btn = tkinter.Button(settings_wnd,
|
||||||
|
command=control_and_save,
|
||||||
|
text=config.SETTINGS_WINDOW["SAVE"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
cancel_btn = tkinter.Button(settings_wnd,
|
||||||
|
command=settings_wnd.destroy,
|
||||||
|
text=config.SETTINGS_WINDOW["CANCEL"],
|
||||||
|
font=config.GUI_FONT)
|
||||||
|
|
||||||
|
# Place all components
|
||||||
|
font_label.place(x=15, y=15)
|
||||||
|
font_size_label.place(x=15, y=55)
|
||||||
|
text_color_label.place(x=15, y=95)
|
||||||
|
bg_color_label.place(x=15, y=135)
|
||||||
|
layout_label.place(x=15, y=175)
|
||||||
|
newline_label.place(x=15, y=215)
|
||||||
|
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=208, y=15)
|
||||||
|
font_size_entry.place(x=210, y=55)
|
||||||
|
text_color.place(x=210, y=95)
|
||||||
|
bg_color.place(x=210, y=135)
|
||||||
|
double_layout_btn.place(x=210, y=175)
|
||||||
|
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=350)
|
||||||
|
cancel_btn.place(x=190, y=350)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_color(color):
|
||||||
|
"""
|
||||||
|
Returns whether or not given color is valid in the hexadecimal format.
|
||||||
|
"""
|
||||||
|
return isinstance(color, str) and len(color) == 7 and color[0] == "#"
|
||||||
|
|
||||||
|
|
||||||
|
def validate_font_size(size):
|
||||||
|
"""
|
||||||
|
Returns whether or not given size is an acceptable font size.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
size = int(size)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return 0 < size < 70
|
||||||
|
|
||||||
|
|
||||||
|
def validate_server_port(port):
|
||||||
|
"""
|
||||||
|
Returns Whether or not gicen port is a valid server port.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
int(port)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def save_settings(settings):
|
||||||
|
"""
|
||||||
|
Saves given settings to the settings file.
|
||||||
|
"""
|
||||||
|
file_content = ""
|
||||||
|
|
||||||
|
for key in settings.keys():
|
||||||
|
file_content += key + "=" + settings[key] + "\n"
|
||||||
|
|
||||||
|
set_settings_file_content(file_content)
|
||||||
|
|
||||||
|
|
||||||
|
def decode_boolean_setting(setting):
|
||||||
|
"""
|
||||||
|
Decodes a boolean string of "True" or "False"
|
||||||
|
to the coorect boolean value.
|
||||||
|
"""
|
||||||
|
return setting == "True"
|
||||||
|
|
||||||
|
|
||||||
|
def encode_boolean_setting(value):
|
||||||
|
"""
|
||||||
|
Encodes a boolean to the string "True" or "False".
|
||||||
|
"""
|
||||||
|
if value:
|
||||||
|
return "True"
|
||||||
|
else:
|
||||||
|
return "False"
|
||||||
|
|
||||||
|
|
||||||
|
def validate_pixels(pixels):
|
||||||
|
"""
|
||||||
|
Checks if given string can be used as a pixel value for height or width.
|
||||||
|
Height or Width or assumed to never surpass 10000
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
pixels = int(pixels)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return 0 < pixels < 10000
|
||||||
|
|
||||||
|
|
||||||
|
def validate_separator(separator):
|
||||||
|
return separator.strip()
|
||||||
Reference in New Issue
Block a user