fix some ui issues, add scrolling to presets area, update readme instructions, other small QOL improvements
This commit is contained in:
31
Readme.md
31
Readme.md
@@ -4,7 +4,7 @@
|
||||
|
||||
**Text to Mic** is a powerful, user-friendly application that seamlessly converts written text into natural-sounding speech, playing it over a virtual microphone. This tool is perfect for situations where you want to automate voice output — whether it's for presentations, online meetings, voiceovers, or accessibility purposes.
|
||||
|
||||

|
||||

|
||||
|
||||
More info about Text to Mic including a changelog can be found here:
|
||||
https://www.scorchsoft.com/blog/text-to-mic-for-meetings/
|
||||
@@ -50,6 +50,35 @@ https://platform.openai.com/docs/quickstart/account-setup
|
||||
|
||||
This tool was brought to you by Scorchsoft - We build custom apps to your requirements. Please contact us if you have a requirement for a custom app project.
|
||||
|
||||
## Advanced Tips
|
||||
|
||||
|
||||
### 1. ChatGPT AI Manipulation
|
||||
|
||||
If you go to "Settings > ChatGPT Manipulation" then you can turn this on and pick which model to use.
|
||||
|
||||

|
||||
|
||||
If enabled (both enabled and "auto apply to recorded transcript"), this will run your transcript through AI with the desired prompt each time you record your voice and convert it to text.
|
||||
|
||||
If you've enabled but not turned on auto apply, then you can manually trigger this action to any text you've input into "text to Read" via the context menu "Input > Apply AI manipulation to text input". This will only work if you've turned it on and added your API key
|
||||
|
||||
### 2. Hotkeys
|
||||
|
||||
You can use a hotkey combination to quickly trigger recording and playing of recorded text. By default the hot keys are "ctrl+shift+0" to start the recording, then press it again to stop, transcribe, and submit. "Ctrl+shift+9" stops the recording without playing it. "Ctrl+shift+8" replays the last transcribed or written text.
|
||||
|
||||
"Settings > Hotkey Settings" allows you to customise the hotkey combinations used to trigger the above actions.
|
||||
|
||||
### 3. Presets
|
||||
|
||||
Click the presets button at the bottom of the app to open the presets area.
|
||||
|
||||
Once loaded for the first time, presets are stored in "config/presets.json", this means if you close the app you can edit these and add categories etc via notepad. If you do this, please make sure you don't break or invalidate the json structure.
|
||||
|
||||
You can also edit presets from within the app, but this is limtied to saving new presets to an existing category, favouriting presets, and deleting them. Any other edits have to be completed by editing the json file.
|
||||
|
||||
You can add a new preset by writing it into the "Text to Read" area, then at the top right of the area select the category you wish to add it to, and hit save.
|
||||
|
||||
## Running the CLI Version
|
||||
|
||||
run the executable or "python text-to-mic.py"
|
||||
|
||||
BIN
images/app-screenshot-v1-0-9.png
Normal file
BIN
images/app-screenshot-v1-0-9.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
BIN
images/chatgpt-manipulation.png
Normal file
BIN
images/chatgpt-manipulation.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
559
presets.example.json
Normal file
559
presets.example.json
Normal file
@@ -0,0 +1,559 @@
|
||||
{
|
||||
"presets": [
|
||||
{
|
||||
"category": "Greetings",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Hello, how are you?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Good morning!",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Good evening!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Nice to meet you.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Hi there!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Welcome back!",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Questions",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Could you please repeat that?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Where is the nearest restroom?",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "What time is it?",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Can I help you with something?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Testing 123",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "How does this work?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Can you explain that further?",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Responses",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Thank you!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "You're welcome.",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "No problem.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I understand.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Absolutely!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I agree.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Gaming",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Watch out, behind you!",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Need backup here.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Let's stick together.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "GG (Good Game)!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Enemy spotted!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Level up!",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Directions",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Go straight ahead.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Turn left at the next corner.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "It's on your right.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Follow me, please.",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Take the second exit.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "You will see it on the left.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Requests",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Could you assist me with this?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Please let me know if I can help.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "May I have some assistance?",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Could you lower your voice?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Can you pass the salt?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Please wait a moment.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Social",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "How was your day?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "It's nice to see you!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I hope you're doing well.",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Take care.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "What's new with you?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Long time no see!",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Apologies",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "I'm sorry.",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Excuse me, please.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I didn't mean to.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Pardon me.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "My apologies.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Sorry for the inconvenience.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Encouragement",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Great job!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "You can do it!",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Keep going!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Well done!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Don't give up!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "You're making progress.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Emergency",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "I need help immediately!",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Call 999!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I'm not feeling well.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Please get help.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "There's been an accident.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I require urgent assistance.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Farewells",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Goodbye!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Take care!",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "See you later.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Have a great day!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Farewell!",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Until next time.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Business",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Let's schedule a meeting.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I will get back to you on that.",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Could you send me the report?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Thank you for your cooperation.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Let's discuss this further.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Please review the attached document.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Travel",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Where is the nearest bus stop?",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "I need a taxi.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "What is the flight status?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Can you help me with my luggage?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Where can I buy a ticket?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Is this seat taken?",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Healthcare",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "I need to see a doctor.",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "I have a headache.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Where is the nearest hospital?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I need medication.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I'm feeling dizzy.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I need an ambulance.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Education",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Can you help me with my homework?",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "What is the assignment due date?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I need tutoring in math.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Where is the library?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Can I join the study group?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "When is the next exam?",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Shopping",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "How much does this cost?",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "Do you have this in a different size?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I'd like to return this item.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Can I pay with a credit card?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Where is the fitting room?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Do you offer a warranty?",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Technology",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "How do I reset my password?",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "My device is not working.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Can you help me install this software?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Where can I find the user manual?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "The screen is frozen.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I forgot my username.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Personal",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "I need to buy groceries.",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "I'm going to the gym.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Can we meet up later?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I need some alone time.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I'm cooking dinner.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I have a doctor's appointment.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "Entertainment",
|
||||
"phrases": [
|
||||
{
|
||||
"text": "Let's watch a movie.",
|
||||
"isFavourite": true
|
||||
},
|
||||
{
|
||||
"text": "I'm listening to music.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Do you want to play a game?",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I'm reading a book.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "Let's go to a concert.",
|
||||
"isFavourite": false
|
||||
},
|
||||
{
|
||||
"text": "I'm streaming a new series.",
|
||||
"isFavourite": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -30,10 +30,11 @@ class TextToMic(tk.Tk):
|
||||
super().__init__()
|
||||
|
||||
self.title("Scorchsoft Text to Mic")
|
||||
self.default_geometry = "590x800"
|
||||
self.default_geometry = "590x750"
|
||||
self.geometry(self.default_geometry)
|
||||
|
||||
|
||||
self.available_models = ["gpt-4o-mini", "gpt-4o", "gpt-4-turbo"]
|
||||
self.default_model = "gpt-4o-mini"
|
||||
|
||||
self.style = ttk.Style(self)
|
||||
if self.tk.call('tk', 'windowingsystem') == 'aqua':
|
||||
@@ -88,7 +89,7 @@ class TextToMic(tk.Tk):
|
||||
instruction_window.title("App Version")
|
||||
instruction_window.geometry("300x150") # Width x Height
|
||||
|
||||
instructions = """Version 1.1.0\n\n App by Scorchsoft.com"""
|
||||
instructions = """Version 1.2.0\n\n App by Scorchsoft.com"""
|
||||
|
||||
tk.Label(instruction_window, text=instructions, justify=tk.LEFT, wraplength=280).pack(padx=10, pady=10)
|
||||
|
||||
@@ -324,7 +325,7 @@ class TextToMic(tk.Tk):
|
||||
save_button.grid(column=1, row=0, sticky=tk.E) # Align to the right
|
||||
|
||||
# Specify the text to read
|
||||
self.text_input = tk.Text(main_frame, height=10, width=68)
|
||||
self.text_input = tk.Text(main_frame, height=5, width=68)
|
||||
self.text_input.grid(column=0, row=6, columnspan=2, pady=(0, 20), sticky="nsew") # Fill available width
|
||||
|
||||
|
||||
@@ -380,7 +381,7 @@ class TextToMic(tk.Tk):
|
||||
self.right_arrow.pack(side=tk.RIGHT, padx=1) # Reduced padding
|
||||
|
||||
# Presets display area with a fixed height and vertical scrollbar
|
||||
self.presets_canvas = Canvas(self.presets_frame, height=200, width=self.presets_frame.winfo_width())
|
||||
self.presets_canvas = Canvas(self.presets_frame, height=250, width=self.presets_frame.winfo_width())
|
||||
self.presets_scrollbar = Scrollbar(self.presets_frame, orient="vertical", command=self.presets_canvas.yview)
|
||||
self.presets_canvas.configure(yscrollcommand=self.presets_scrollbar.set)
|
||||
|
||||
@@ -400,6 +401,8 @@ class TextToMic(tk.Tk):
|
||||
self.refresh_presets_display()
|
||||
self.toggle_presets()
|
||||
self.toggle_presets()
|
||||
self.enable_mouse_wheel_scrolling()
|
||||
|
||||
|
||||
|
||||
def scroll_left(self):
|
||||
@@ -409,8 +412,36 @@ class TextToMic(tk.Tk):
|
||||
self.tabs_canvas.xview_scroll(5, "units")
|
||||
|
||||
|
||||
def enable_mouse_wheel_scrolling(self):
|
||||
"""Enable conditional mouse wheel scrolling for the presets canvas and category tabs canvas."""
|
||||
|
||||
def on_vertical_scroll(event):
|
||||
# Scroll the presets_canvas vertically
|
||||
if event.num == 4: # macOS scroll up
|
||||
self.presets_canvas.yview_scroll(-1, "units")
|
||||
elif event.num == 5: # macOS scroll down
|
||||
self.presets_canvas.yview_scroll(1, "units")
|
||||
else: # Windows and Linux
|
||||
self.presets_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
|
||||
|
||||
def on_horizontal_scroll(event):
|
||||
# Scroll the tabs_canvas horizontally
|
||||
if event.num == 4: # macOS scroll left
|
||||
self.tabs_canvas.xview_scroll(-1, "units")
|
||||
elif event.num == 5: # macOS scroll right
|
||||
self.tabs_canvas.xview_scroll(1, "units")
|
||||
else: # Windows and Linux
|
||||
self.tabs_canvas.xview_scroll(int(-1 * (event.delta / 120)), "units")
|
||||
|
||||
# Bind scroll events when mouse enters or leaves the presets canvas area
|
||||
self.presets_canvas.bind("<Enter>", lambda e: self.presets_canvas.bind_all("<MouseWheel>", on_vertical_scroll))
|
||||
self.presets_canvas.bind("<Leave>", lambda e: self.presets_canvas.unbind_all("<MouseWheel>"))
|
||||
|
||||
# Bind scroll events when mouse enters or leaves the tabs canvas area
|
||||
self.tabs_canvas.bind("<Enter>", lambda e: self.tabs_canvas.bind_all("<MouseWheel>", on_horizontal_scroll))
|
||||
self.tabs_canvas.bind("<Leave>", lambda e: self.tabs_canvas.unbind_all("<MouseWheel>"))
|
||||
|
||||
|
||||
|
||||
|
||||
def open_scorchsoft(self, event=None):
|
||||
webbrowser.open('https://www.scorchsoft.com')
|
||||
@@ -975,7 +1006,7 @@ Please also make sure you read the Terms of use and licence statement before usi
|
||||
# Default settings
|
||||
settings = {
|
||||
"chat_gpt_completion": False,
|
||||
"model": "gpt-4o-mini",
|
||||
"model": self.default_model,
|
||||
"prompt": "",
|
||||
"auto_apply_ai_to_recording": False,
|
||||
"hotkeys": {
|
||||
@@ -1016,10 +1047,13 @@ Please also make sure you read the Terms of use and licence statement before usi
|
||||
enable_completion = tk.BooleanVar(value=settings.get("chat_gpt_completion", False))
|
||||
ttk.Checkbutton(main_frame, text="Enable ChatGPT Completion", variable=enable_completion).grid(row=0, column=1, sticky=tk.W, pady=2)
|
||||
|
||||
# Model selection
|
||||
model_var = tk.StringVar(value=settings.get("model", "gpt-3.5-turbo"))
|
||||
# Model selection#
|
||||
|
||||
|
||||
model_var = tk.StringVar(value=settings.get("model", self.default_model))
|
||||
ttk.Label(main_frame, text="Model:").grid(row=1, column=0, sticky=tk.W, pady=2)
|
||||
ttk.OptionMenu(main_frame, model_var, "gpt-4o-mini", "gpt-4o", "gpt-4-turbo").grid(row=1, column=1, sticky=tk.W, pady=2)
|
||||
model_menu = ttk.OptionMenu(main_frame, model_var, model_var.get(), *self.available_models)
|
||||
model_menu.grid(row=1, column=1, sticky=tk.W, pady=2)
|
||||
|
||||
# Max Tokens selection
|
||||
max_tokens_var = tk.IntVar(value=settings.get("max_tokens", 750))
|
||||
@@ -1173,29 +1207,29 @@ Please also make sure you read the Terms of use and licence statement before usi
|
||||
bottom_frame = ttk.Frame(frame)
|
||||
bottom_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=2)
|
||||
|
||||
self.style.configure("Flat.TButton",
|
||||
borderwidth=0,
|
||||
highlightthickness=0,
|
||||
font=("Arial", 12), # Adjust font and size
|
||||
anchor="center") # Center text
|
||||
|
||||
|
||||
# Favourite button
|
||||
fav_icon = "❤️" if phrase["isFavourite"] else "♡"
|
||||
fav_btn = ttk.Button(bottom_frame, text=fav_icon, command=lambda p=phrase: self.toggle_favourite(p), width=3, style="Flat.TButton")
|
||||
fav_btn = ttk.Button(bottom_frame, text=fav_icon, command=lambda p=phrase: self.toggle_favourite(p), width=2, style="Flat.TButton")
|
||||
fav_btn.pack(side=tk.RIGHT, padx=2)
|
||||
#fav_btn.config(borderwidth=0, highlightthickness=0) # Remove border
|
||||
|
||||
# Delete button
|
||||
del_btn = ttk.Button(bottom_frame, text="🗑️", command=lambda t=phrase["text"]: self.delete_preset(self.current_category, t), width=3, style="Flat.TButton")
|
||||
del_btn = ttk.Button(bottom_frame, text="🗑️", command=lambda t=phrase["text"]: self.delete_preset(self.current_category, t), width=2, style="Flat.TButton")
|
||||
del_btn.pack(side=tk.RIGHT, padx=2)
|
||||
#del_btn.config(borderwidth=0, highlightthickness=0) # Remove border
|
||||
|
||||
|
||||
# Update scroll region after populating all items
|
||||
self.presets_canvas.configure(scrollregion=self.presets_canvas.bbox("all"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def wrap_text(self, text, max_lines=3, max_chars_per_line=20):
|
||||
"""Wrap text to fit within a limited number of lines and characters."""
|
||||
words = text.split()
|
||||
@@ -1246,7 +1280,7 @@ Please also make sure you read the Terms of use and licence statement before usi
|
||||
else:
|
||||
self.presets_frame.grid_remove()
|
||||
self.presets_button.config(text="▶ Presets")
|
||||
self.geometry("590x520")
|
||||
self.geometry("590x460")
|
||||
self.presets_collapsed = not self.presets_collapsed
|
||||
|
||||
|
||||
@@ -1263,14 +1297,32 @@ Please also make sure you read the Terms of use and licence statement before usi
|
||||
|
||||
|
||||
def load_presets(self):
|
||||
"""Load presets from the JSON file."""
|
||||
"""Load presets from the JSON file, copying from example if necessary."""
|
||||
presets_path = Path("config/presets.json")
|
||||
example_path = self.resource_path("presets.example.json") # Path for the example file
|
||||
|
||||
# Check if presets.json exists, and if not, copy presets.example.json to config
|
||||
if not presets_path.exists():
|
||||
try:
|
||||
# Ensure config directory exists
|
||||
presets_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy example presets to config directory
|
||||
with open(example_path, "r", encoding="utf-8") as example_file:
|
||||
with open(presets_path, "w", encoding="utf-8") as config_file:
|
||||
config_file.write(example_file.read())
|
||||
except Exception as e:
|
||||
messagebox.showerror("Error", f"Failed to copy example presets: {e}")
|
||||
return [] # Return empty if unable to load or copy presets
|
||||
|
||||
# Load presets.json as usual
|
||||
try:
|
||||
with open("config/presets.json", "r") as f:
|
||||
with open(presets_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
return data.get("presets", [])
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
# Create a default structure if the file doesn't exist or is corrupted
|
||||
return []
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
messagebox.showerror("Error", f"Error loading presets: {e}")
|
||||
return [] # Default to empty if load fails
|
||||
|
||||
def save_presets(self):
|
||||
"""Save presets to the JSON file."""
|
||||
|
||||
Reference in New Issue
Block a user