Spaces:
Build error
Build error
try: | |
from .agent.chat_history import * | |
from .agent.assistant import * | |
from .llm import * | |
from .llm_settings import llm_settings | |
from .agent.agent import * | |
from .agent.background import * | |
from .gui.signal import * | |
from .gui.button import * | |
from .gui.settings import settings_popup | |
from .gui.llmsettings import llmsettings_popup | |
from .utils.db import * | |
from .utils.telemetry import my_tracer, os_name | |
from .audio.wake_word import wake_word | |
from .audio.tts import text_to_speech | |
from .character import name, developer | |
except ImportError: | |
# This is for running the script directly | |
# in order to test the GUI without rebuilding the package | |
from agent.chat_history import * | |
from agent.assistant import * | |
from llm import * | |
from llm_settings import llm_settings | |
from agent.agent import * | |
from agent.background import * | |
from utils.db import * | |
from gui.signal import * | |
from gui.button import * | |
from gui.settings import settings_popup | |
from gui.llmsettings import llmsettings_popup | |
from utils.telemetry import my_tracer, os_name | |
from audio.wake_word import wake_word | |
from audio.tts import text_to_speech | |
import platform | |
import threading | |
import time | |
import random | |
import math | |
from PyQt5.QtWidgets import QMainWindow, QVBoxLayout, QWidget | |
from PyQt5.QtGui import QMouseEvent, QPainter, QPen, QBrush, QIcon, QColor | |
from PyQt5.QtCore import Qt, QTimer, QRect, pyqtSignal | |
from PyQt5.QtGui import QKeySequence | |
from PyQt5.QtWidgets import QShortcut | |
from PyQt5.QtWidgets import QSpacerItem, QSizePolicy | |
from PyQt5.QtWidgets import QDesktopWidget | |
from PyQt5.QtWidgets import ( | |
QPushButton, | |
QLabel, | |
QHBoxLayout, | |
) | |
from PyQt5.QtCore import QPoint | |
from PyQt5.QtWidgets import QTextEdit | |
from PyQt5 import QtGui | |
from PyQt5.QtCore import QThread | |
import pygame | |
print("Imported all libraries") | |
from PyQt5 import QtCore | |
try: | |
import ctypes | |
myappid = "khulnasoft.gpt_computer_agent.gui.1" | |
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) | |
except: | |
pass | |
the_input_box = None | |
the_input_text = None | |
the_input_box_pre = None | |
the_main_window = None | |
user_id = load_user_id() | |
os_name_ = os_name() | |
from PyQt5.QtCore import QRegExp | |
from PyQt5.QtGui import QSyntaxHighlighter, QTextCharFormat, QFont | |
class PythonSyntaxHighlighter(QSyntaxHighlighter): | |
def __init__(self, parent): | |
super().__init__(parent) | |
self.highlighting_rules = [] | |
# Define different text formats with appropriate colors | |
keyword_format = QTextCharFormat() | |
keyword_format.setForeground(QColor(127, 0, 85)) # Dark purple for keywords | |
built_in_formats = QTextCharFormat() | |
built_in_formats.setForeground( | |
QColor(42, 0, 255) | |
) # Dark blue for built-ins and constants | |
string_format = QTextCharFormat() | |
string_format.setForeground(QColor(0, 128, 0)) # Green for strings | |
function_format = QTextCharFormat() | |
function_format.setForeground(QColor(0, 0, 255)) # Blue for function names | |
comment_format = QTextCharFormat() | |
comment_format.setForeground(QColor(128, 128, 128)) # Gray for comments | |
comment_format.setFontItalic(True) | |
number_format = QTextCharFormat() | |
number_format.setForeground(QColor(255, 0, 0)) # Red for numbers | |
decorator_format = QTextCharFormat() | |
decorator_format.setForeground(QColor(0, 0, 128)) # Navy blue for decorators | |
# Markdown formatting | |
header_format = QTextCharFormat() | |
header_format.setForeground(QColor(0, 128, 128)) # Teal for headers | |
header_format.setFontWeight(QFont.Bold) | |
bold_format = QTextCharFormat() | |
bold_format.setFontWeight(QFont.Bold) | |
italic_format = QTextCharFormat() | |
italic_format.setFontItalic(True) | |
code_format = QTextCharFormat() | |
code_format.setForeground(QColor(255, 140, 0)) # Dark orange for inline code | |
code_format.setFontFamily("Courier New") | |
code_format.setBackground( | |
QColor(245, 245, 245) | |
) # Light gray background for inline code | |
block_code_format = QTextCharFormat() | |
block_code_format.setForeground( | |
QColor(255, 140, 0) | |
) # Dark orange for code blocks | |
block_code_format.setFontFamily("Courier New") | |
block_code_format.setBackground( | |
QColor(245, 245, 245) | |
) # Light gray background for code blocks | |
# Define the regular expressions | |
keywords = [ | |
"def", | |
"class", | |
"if", | |
"else", | |
"elif", | |
"return", | |
"import", | |
"from", | |
"as", | |
"for", | |
"while", | |
"try", | |
"except", | |
"finally", | |
"with", | |
"async", | |
"await", | |
"yield", | |
"lambda", | |
"global", | |
"nonlocal", | |
"assert", | |
"del", | |
"pass", | |
"break", | |
"continue", | |
"and", | |
"or", | |
"not", | |
"is", | |
"in", | |
] | |
self.highlighting_rules += [ | |
(QRegExp(r"\b" + word + r"\b"), keyword_format) for word in keywords | |
] | |
built_ins = [ | |
"True", | |
"False", | |
"None", | |
"__init__", | |
"self", | |
"print", | |
"len", | |
"range", | |
"str", | |
"int", | |
"float", | |
"list", | |
"dict", | |
"set", | |
"tuple", | |
] | |
self.highlighting_rules += [ | |
(QRegExp(r"\b" + word + r"\b"), built_in_formats) for word in built_ins | |
] | |
self.highlighting_rules.append( | |
(QRegExp(r'"[^"\\]*(\\.[^"\\]*)*"'), string_format) | |
) | |
self.highlighting_rules.append( | |
(QRegExp(r"'[^'\\]*(\\.[^'\\]*)*'"), string_format) | |
) | |
self.highlighting_rules.append((QRegExp(r"\bdef\b\s*(\w+)"), function_format)) | |
self.highlighting_rules.append((QRegExp(r"\bclass\b\s*(\w+)"), function_format)) | |
self.highlighting_rules.append((QRegExp(r"#.*"), comment_format)) | |
self.highlighting_rules.append((QRegExp(r"\b[0-9]+[lL]?\b"), number_format)) | |
self.highlighting_rules.append( | |
(QRegExp(r"\b0[xX][0-9A-Fa-f]+[lL]?\b"), number_format) | |
) | |
self.highlighting_rules.append( | |
(QRegExp(r"\b0[oO]?[0-7]+[lL]?\b"), number_format) | |
) | |
self.highlighting_rules.append((QRegExp(r"\b0[bB][01]+[lL]?\b"), number_format)) | |
self.highlighting_rules.append((QRegExp(r"@[^\s]+"), decorator_format)) | |
# Markdown rules | |
self.highlighting_rules.append( | |
(QRegExp(r"^#{1,6} .+"), header_format) | |
) # Headers | |
self.highlighting_rules.append( | |
(QRegExp(r"\*\*[^*]+\*\*"), bold_format) | |
) # **bold** | |
self.highlighting_rules.append((QRegExp(r"__[^_]+__"), bold_format)) # __bold__ | |
self.highlighting_rules.append( | |
(QRegExp(r"\*[^*]+\*"), italic_format) | |
) # *italic* | |
self.highlighting_rules.append((QRegExp(r"_[^_]+_"), italic_format)) # _italic_ | |
self.highlighting_rules.append( | |
(QRegExp(r"`[^`]+`"), code_format) | |
) # Inline code | |
def highlightBlock(self, text): | |
# Handle code blocks separately | |
if text.strip().startswith("```"): | |
self.setFormat(0, len(text), self.highlighting_rules[-1][1]) | |
return | |
for pattern, format in self.highlighting_rules: | |
expression = QRegExp(pattern) | |
index = expression.indexIn(text) | |
while index >= 0: | |
length = expression.matchedLength() | |
self.setFormat(index, length, format) | |
index = expression.indexIn(text, index + length) | |
readed_sentences = [] | |
import re | |
def split_with_multiple_delimiters(text, delimiters): | |
""" | |
Splits the text by any of the given delimiters while keeping the delimiters in the resulting parts. | |
:param text: The input text to be split. | |
:param delimiters: A string of delimiters to split the text on. | |
:return: A list of parts including the delimiters. | |
""" | |
# Create a regular expression pattern that matches any of the delimiters | |
pattern = re.compile(f"(.*?[{re.escape(delimiters)}])") | |
parts = pattern.findall(text) | |
# Check if the last part is not complete and remove it if necessary | |
if ( | |
parts | |
and text | |
and not any(text.endswith(d) for d in delimiters) | |
and parts | |
and not any(parts[-1].endswith(d) for d in delimiters) | |
): | |
parts.pop() | |
return parts | |
def click_sound(): | |
pygame.mixer.init() | |
retro = pygame.mixer.Sound(click_sound_path) | |
retro.set_volume(0.1) | |
retro.play() | |
class Worker(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
self.make_animation = True | |
self.commited_text = [] | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
last_text = ( | |
self.commited_text[-1] if len(self.commited_text) > 0 else "" | |
) | |
if self.the_input_text != last_text: | |
self.commited_text.append(self.the_input_text) | |
if len(self.the_input_text) > 90 or not self.make_animation: | |
self.text_to_set.emit(self.the_input_text) | |
else: | |
for i in range(len(self.the_input_text)): | |
self.text_to_set.emit(self.the_input_text[: i + 1]) | |
self.msleep(10) | |
return_key_event = None | |
class CustomTextEdit(QTextEdit): | |
def __init__(self, parent=None): | |
super(CustomTextEdit, self).__init__(parent) | |
def keyPressEvent(self, event): | |
if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: | |
global return_key_event | |
return_key_event() | |
super(CustomTextEdit, self).keyPressEvent( | |
event | |
) # Process other key events normally | |
class Worker_2(QThread): | |
text_to_set = pyqtSignal(str) | |
text_to_set_title_bar = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
self.title_bar_text = None | |
self.prev = None | |
self.commited_text = [] | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text and ( | |
self.prev is None or self.prev != self.the_input_text | |
): | |
self.prev = self.the_input_text | |
self.text_to_set.emit("True") | |
for i in range(len(self.title_bar_text)): | |
self.text_to_set_title_bar.emit(self.title_bar_text[: i + 1]) | |
self.msleep(10) | |
if not self.the_input_text and self.prev != self.the_input_text: | |
self.prev = self.the_input_text | |
self.text_to_set.emit("False") | |
the_text = " " + name() | |
for i in range(len(the_text)): | |
self.text_to_set_title_bar.emit(the_text[: i + 1]) | |
self.msleep(10) | |
class Worker_3(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
self.text_to_set.emit("True") | |
self.the_input_text = None | |
class Worker_collapse(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
self.text_to_set.emit("True") | |
self.the_input_text = None | |
class Worker_uncollapse(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
self.text_to_set.emit("True") | |
self.the_input_text = None | |
class Worker_show_logo(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
self.text_to_set.emit("True") | |
self.the_input_text = None | |
class Worker_hide_logo(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
self.text_to_set.emit("True") | |
self.the_input_text = None | |
class Worker_activate_long_gca(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
self.text_to_set.emit("True") | |
self.the_input_text = None | |
class Worker_deactivate_long_gca(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
self.text_to_set.emit("True") | |
self.the_input_text = None | |
class Worker_tray_and_task_bar_logo(QThread): | |
text_to_set = pyqtSignal(str) | |
def __init__(self): | |
super().__init__() | |
self.the_input_text = None | |
def run(self): | |
while True: | |
self.msleep(500) # Simulate a time-consuming task | |
if self.the_input_text: | |
self.text_to_set.emit("True") | |
self.the_input_text = None | |
class DrawingWidget(QWidget): | |
def __init__(self, parent=None): | |
super(DrawingWidget, self).__init__(parent) | |
# Set widget properties if needed, e.g., size | |
self.main_ = parent | |
self.active_button = "" | |
def paintEvent(self, event): | |
if llm_settings[load_model_settings()]["vision"] is True: | |
self.main_.screen_available = True | |
else: | |
self.main_.screen_available = False | |
self.main_.setAutoFillBackground(True) | |
painter = QPainter(self) | |
painter = painter | |
painter.setRenderHint(QPainter.Antialiasing) | |
painter.setPen(QPen(QColor("#000"), 1)) | |
painter.setBrush(QBrush(Qt.black, Qt.SolidPattern)) | |
center_x = 95 | |
center_y = 40 | |
if "talking" in self.main_.state: | |
# Draw a pulsating circle with smooth easing animation | |
radius_variation = 5 * ( | |
1 + math.sin(self.main_.pulse_frame * math.pi / 100) | |
) | |
radius = 70 + radius_variation | |
painter.drawEllipse( | |
int(center_x - radius / 2), | |
int(center_y - radius / 2), | |
int(radius), | |
int(radius), | |
) | |
elif self.main_.state == "thinking": | |
# more slow pulsating circle with smooth easing animation | |
radius_variation = 5 * ( | |
1 + math.sin(self.main_.pulse_frame * math.pi / 100) | |
) | |
radius = 70 + radius_variation | |
painter.drawEllipse( | |
int(center_x - radius / 2), | |
int(center_y - radius / 2), | |
int(radius), | |
int(radius), | |
) | |
else: | |
radius = 70 | |
if self.main_.screen_available: | |
painter.drawEllipse( # Main Button | |
int(center_x - radius / 2), | |
int(center_y - radius / 2), | |
int(radius), | |
int(radius), | |
) | |
self.main_.circle_rect = QRect( | |
int(center_x - radius / 2), | |
int(center_y - radius / 2), | |
int(radius), | |
int(radius), | |
) | |
if not self.main_.state == "thinking": | |
painter.setPen(QPen(QColor("#01EE8A"), 1)) | |
if self.main_.screen_available: | |
painter.drawEllipse( # Main BUtton Green Border | |
int(center_x - radius / 2), | |
int(center_y - radius / 2), | |
int(radius), | |
int(radius), | |
) | |
else: | |
painter.setPen(QPen(QColor("#23538F"), 1)) | |
painter.drawEllipse( | |
int(center_x - radius / 2), | |
int(center_y - radius / 2), | |
int(radius), | |
int(radius), | |
) | |
painter.setPen(QPen(QColor("#000"), 1)) | |
small_center_x = 165 | |
small_center_y = 25 | |
small_radius = 30 | |
painter.drawEllipse( # Microphone bacground black | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
) | |
self.main_.small_circle_rect = QRect( | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
) | |
# Draw the icon inside the circle | |
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle | |
icon_rect = QRect( | |
small_center_x - icon_size // 2, | |
small_center_y - icon_size // 2, | |
icon_size, | |
icon_size, | |
) | |
self.main_.small_circle_recticon = QIcon(microphone_icon_path) | |
self.main_.small_circle_recticon.paint(painter, icon_rect) | |
small_center_x = 30 | |
small_center_y = 60 | |
small_radius = 30 | |
painter.drawEllipse( | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
) | |
self.main_.small_circle_left = QRect( | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
) | |
# Draw the icon inside the circle | |
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle | |
icon_rect = QRect( | |
small_center_x - icon_size // 2, | |
small_center_y - icon_size // 2, | |
icon_size, | |
icon_size, | |
) | |
self.main_.small_circle_lefticon = QIcon(audio_icon_path) | |
self.main_.small_circle_lefticon.paint(painter, icon_rect) | |
small_center_x = 30 | |
small_center_y = 25 | |
small_radius = 30 | |
if self.main_.screen_available: | |
painter.drawEllipse( # ScreenShot BUtton | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
) | |
self.main_.small_circle_left_top = QRect( | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
) | |
self.main_.screenshot_button_coordinates_size = [ | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
] | |
if self.active_button == "screenshot": | |
self.screenshot_button_border_activate(painter) | |
self.active_button = "" | |
if self.main_.screen_available: | |
# Draw the icon inside the circle | |
icon_size = ( | |
small_radius * 2 // 3 | |
) # Adjust the icon size relative to the circle | |
icon_rect = QRect( | |
small_center_x - icon_size // 2, | |
small_center_y - icon_size // 2, | |
icon_size, | |
icon_size, | |
) | |
self.main_.small_circle_left_topticon = QIcon(screenshot_icon_path) | |
self.main_.small_circle_left_topticon.paint(painter, icon_rect) | |
small_center_x = 165 | |
small_center_y = 60 | |
small_radius = 30 | |
painter.drawEllipse( | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
) | |
self.main_.small_circle_collapse = QRect( | |
int(small_center_x - small_radius / 2), | |
int(small_center_y - small_radius / 2), | |
int(small_radius), | |
int(small_radius), | |
) | |
# Draw the icon inside the circle | |
icon_size = small_radius * 2 // 3 # Adjust the icon size relative to the circle | |
icon_rect = QRect( | |
small_center_x - icon_size // 2, | |
small_center_y - icon_size // 2, | |
icon_size, | |
icon_size, | |
) | |
if is_collapse_setting_active(): | |
self.main_.small_circle_collapse_icon = QIcon(down_icon_path) | |
if not is_collapse_setting_active() and is_long_gca_setting_active(): | |
self.main_.small_circle_collapse_icon = QIcon(up_icon_path) | |
if not is_collapse_setting_active() and not is_long_gca_setting_active(): | |
self.main_.small_circle_collapse_icon = QIcon(down_icon_path) | |
self.main_.small_circle_collapse_icon.paint(painter, icon_rect) | |
def screenshot_button_border_activate(self, painter): | |
# Add an white border to the circle | |
painter.setPen(QPen(QColor("#FFF"), 1)) | |
# Draw the ellipse with the specified green border | |
self.main_.screenshot_button_border = painter.drawEllipse( | |
self.main_.screenshot_button_coordinates_size[0], | |
self.main_.screenshot_button_coordinates_size[1], | |
self.main_.screenshot_button_coordinates_size[2], | |
self.main_.screenshot_button_coordinates_size[3], | |
) | |
painter.setPen(QPen(QColor("#000"), 1)) | |
def mousePressEvent(self, event: QMouseEvent): | |
self.main_.old_position = event.globalPos() | |
with my_tracer.start_span("mouse_press_event") as span: | |
span.set_attribute("user_id", user_id) | |
span.set_attribute("os_name", os_name_) | |
if self.main_.state == "idle" or "talking" in self.main_.state: | |
try: | |
if self.main_.circle_rect.contains(event.pos()): | |
if self.main_.state == "aitalking": | |
self.main_.stop_ai_talking() | |
else: | |
self.main_.screenshot_and_microphone_button_action() | |
except: | |
traceback.print_exc() | |
try: | |
if self.main_.small_circle_rect.contains(event.pos()): | |
if self.main_.state == "aitalking": | |
self.main_.stop_ai_talking() | |
else: | |
click_sound() | |
self.main_.button_handler.toggle_recording( | |
no_screenshot=True | |
) | |
except: | |
traceback.print_exc() | |
try: | |
if self.main_.small_circle_left.contains(event.pos()): | |
if self.main_.state == "aitalking": | |
self.main_.stop_ai_talking() | |
else: | |
click_sound() | |
self.main_.button_handler.toggle_recording( | |
take_system_audio=True | |
) | |
except: | |
traceback.print_exc() | |
try: | |
if self.main_.small_circle_left_top.contains(event.pos()): | |
if self.main_.state == "aitalking": | |
self.main_.stop_ai_talking() | |
else: | |
click_sound() | |
self.active_button = "screenshot" | |
self.update() | |
self.main_.button_handler.just_screenshot() | |
except: | |
traceback.print_exc() | |
try: | |
if self.main_.small_circle_collapse.contains(event.pos()): | |
if not is_collapse_setting_active(): | |
if is_long_gca_setting_active(): | |
self.main_.deactivate_long_gca() | |
self.main_.collapse_gca() | |
else: | |
self.main_.activate_long_gca() | |
else: | |
self.main_.uncollapse_gca() | |
self.main_.update() | |
except: | |
pass | |
from PyQt5.QtCore import QVariantAnimation | |
class MainWindow(QMainWindow): | |
api_enabled = False | |
tts_available = True | |
def screenshot_and_microphone_button_action(self): | |
click_sound() | |
if llm_settings[load_model_settings()]["vision"] is True: | |
self.button_handler.toggle_recording(dont_save_image=True) | |
else: | |
self.button_handler.toggle_recording(no_screenshot=True) | |
def stop_ai_talking(self): | |
self.manuel_stop = True | |
self.stop_talking = True | |
def __init__(self): | |
super().__init__() | |
self.background_color = "45, 45, 45" | |
self.opacity = 250 | |
self.border_radius = 10 | |
print("API Enabled:", MainWindow.api_enabled) | |
if MainWindow.api_enabled: | |
try: | |
from .api import start_api | |
start_api() | |
except: | |
raise Exception( | |
"API could not be started, please install gpt-computer-agent[api]" | |
) | |
self.stop_talking = False | |
self.setWindowFlags( | |
Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | |
) # Remove the default title bar | |
# Load the San Francisco font | |
print("Loading font") | |
print(font_dir) | |
try: | |
font_id = QtGui.QFontDatabase.addApplicationFont(font_dir) | |
font_family = QtGui.QFontDatabase.applicationFontFamilies(font_id)[0] | |
self.setFont(QtGui.QFont(font_family)) | |
except: | |
print("Error loading font") | |
self.state = "idle" | |
self.pulse_timer = None | |
self.button_handler = ButtonHandler(self) | |
self.initUI() | |
self.old_position = self.pos() | |
self.collapse = is_collapse_setting_active() | |
if self.collapse: | |
self.collapse_window() | |
global the_main_window | |
the_main_window = self | |
self.general_styling() | |
if is_dark_mode_active(): | |
self.dark_mode() | |
else: | |
self.light_mode() | |
self.wake_word_thread = None | |
self.wake_word_active = False | |
if load_pvporcupine_api_key() != "CHANGE_ME" and is_wake_word_active(): | |
self.wake_word_active = True | |
self.wake_word_trigger() | |
self.manuel_stop = False | |
self.border_animation = None | |
self.complated_answer = False | |
self.reading_thread = False | |
self.reading_thread_2 = False | |
image_layout = QHBoxLayout() | |
self.the_image = QLabel(self) | |
self.the_image.setPixmap(QtGui.QPixmap(load_logo_file_path()).scaled(25, 25)) | |
image_layout.addWidget(self.the_image) | |
self.layout.addLayout(image_layout) | |
self.the_image.setAlignment(Qt.AlignCenter) | |
self.the_image.setFixedHeight(35) | |
# Logo Adding | |
if not is_logo_active_setting_active(): | |
self.the_image.hide() | |
self.update_screen() | |
def put_location(self): | |
if load_location_setting() == "right": | |
self.put_window_to_right_side_of_screen() | |
def init_border_animation(self): | |
# Create a QVariantAnimation to handle color change | |
border_animation = QVariantAnimation( | |
self, | |
valueChanged=self.update_border_color, | |
startValue=QColor("#303030"), | |
endValue=QColor("#23538F"), | |
duration=2000, # Duration for one loop in milliseconds | |
) | |
border_animation.setLoopCount(-1) # Loop indefinitely | |
return border_animation | |
def start_border_animation(self, status): | |
print("FUNCTION TRİGGERED") | |
if self.border_animation is None: | |
self.border_animation = self.init_border_animation() | |
status = status.lower() == "true" | |
if status: | |
self.border_animation.start() | |
else: | |
self.border_animation.stop() | |
self.title_bar.setStyleSheet( | |
"background-color: #2E2E2E; color: white; border-style: solid; border-radius: 15px; border-width: 0px; color: #fff;" | |
) | |
def update_border_color(self, color): | |
self.title_bar.setStyleSheet( | |
f"background-color: #2E2E2E; color: white; border-style: solid; border-radius: 15px; border-width: 2px; border-color: {color.name()}; color: #fff;" | |
) | |
self.title_bar.setStyleSheet( | |
f"background-color: #2E2E2E; color: white; border-style: solid; border-radius: 15px; border-width: 1px; border-color: {color.name()}; color: #fff;" | |
) | |
# Existing methods... | |
def general_styling(self, a=None): | |
self.setAttribute(Qt.WA_TranslucentBackground) | |
self.setStyleSheet( | |
f"border-radius: {self.border_radius}px; background-color: rgba({self.background_color}, {self.opacity});" | |
) | |
self.central_widget.setStyleSheet( | |
"border-style: solid; border-width: 1px; border-color: rgb(0,0,0,0);" | |
) | |
self.input_box_style = "border-radius: 10px; border-bottom: 1px solid #01EE8A;" | |
self.settingsButton_style = ( | |
"border-radius: 5px; height: 25px; border-style: solid;" | |
) | |
self.llmsettingsButton_style = ( | |
"border-radius: 5px; height: 25px; border-style: solid;" | |
) | |
self.btn_minimize.setStyleSheet( | |
"background-color: #2E2E2E; color: white; border-style: none;" | |
) | |
self.btn_close.setStyleSheet( | |
"background-color: #2E2E2E; color: white; border-style: none;" | |
) | |
def set_background_color(self, color): | |
self.background_color = color | |
self.worker_3.the_input_text = "True" | |
def set_opacity(self, opacity): | |
self.opacity = opacity | |
self.worker_3.the_input_text = "True" | |
def set_border_radius(self, radius): | |
self.border_radius = radius | |
self.worker_3.the_input_text = "True" | |
def wake_word_trigger(self): | |
self.wake_word_thread = threading.Thread(target=self.wake_word) | |
self.wake_word_thread.start() | |
def wake_word(self): | |
from .agent.process import tts_if_you_can | |
while True and is_wake_word_active() and self.wake_word_active: | |
if wake_word(self): | |
def random_accept_words(): | |
return random.choice(["Yes", "Sir", "Boss", "Master"]) | |
tts_if_you_can(random_accept_words(), not_threaded=True) | |
def trigger_wake_word(): | |
if ( | |
is_wake_word_screen_setting_active() | |
and llm_settings[load_model_settings()]["vision"] | |
): | |
self.button_handler.toggle_recording(dont_save_image=True) | |
else: | |
self.button_handler.toggle_recording(no_screenshot=True) | |
if self.state == "aitalking": | |
self.manuel_stop = True | |
self.stop_talking = True | |
time.sleep(1) | |
trigger_wake_word() | |
print("Stop talking") | |
else: | |
trigger_wake_word() | |
def dark_mode(self): | |
self.setAutoFillBackground(True) | |
p = self.palette() | |
p.setColor( | |
self.backgroundRole(), QColor("#171717") | |
) # Set background color to white | |
self.setPalette(p) | |
self.input_box.setStyleSheet( | |
self.input_box_style + "background-color: #2E2E2E; color: white;" | |
) | |
self.settingsButton.setStyleSheet( | |
self.settingsButton_style + "background-color: #2E2E2E; color: white;" | |
) | |
self.llmsettingsButton.setStyleSheet( | |
self.llmsettingsButton_style + "background-color: #2E2E2E; color: white;" | |
) | |
def light_mode(self): | |
self.setAutoFillBackground(True) | |
p = self.palette() | |
p.setColor(self.backgroundRole(), QColor("#F0F0F0")) | |
self.setPalette(p) | |
self.input_box.setStyleSheet( | |
self.input_box_style + "background-color: #FFFFFF; color: black;" | |
) | |
self.settingsButton.setStyleSheet( | |
self.settingsButton_style + "background-color: #FFFFFF; color: black; " | |
) | |
self.llmsettingsButton.setStyleSheet( | |
self.llmsettingsButton_style + "background-color: #FFFFFF; color: black; " | |
) | |
def collapse_window(self): | |
the_input_box.hide() | |
self.settingsButton.hide() | |
self.llmsettingsButton.hide() | |
self.update_screen() | |
def initUI(self): | |
self.setWindowTitle("GPT") | |
self.setGeometry(100, 100, 200, 200) | |
width = 210 | |
height = 300 | |
# setting the minimum size | |
self.setMinimumSize(width, height) | |
self.first_height = self.height() | |
self.first_width = self.width() | |
self.central_widget = QWidget(self) | |
self.setCentralWidget(self.central_widget) | |
layout = QVBoxLayout(self.central_widget) | |
# Custom title bar | |
self.title_bar = QWidget(self) | |
self.title_bar.setFixedHeight(30) # Set a fixed height for the title bar | |
self.title_bar.setStyleSheet( | |
"background-color: #2E2E2E; color: #fff; border-radius: 15px; border-style: solid; border-width: 1px; border-color: #303030;" | |
) | |
self.title_bar_layout = QHBoxLayout(self.title_bar) | |
self.title_bar_layout.setContentsMargins(5, 5, 0, 5) | |
self.title_bar_layout.setSpacing(0) | |
self.btn_minimize = QPushButton("-", self.title_bar) | |
self.btn_minimize.setFixedSize(20, 20) | |
self.btn_minimize.clicked.connect(self.showMinimized) | |
def stop_app(): | |
self.stop_talking = True | |
self.wake_word_active = False | |
if MainWindow.api_enabled: | |
from .api import stop_api | |
stop_api() | |
self.close() | |
self.btn_close = QPushButton("×", self.title_bar) | |
self.btn_close.setFixedSize(20, 20) | |
self.btn_close.clicked.connect(stop_app) | |
self.title_label = QLabel(" " + name(), self.title_bar) | |
# Change font size | |
font = QtGui.QFont() | |
font.setPointSize(11) | |
self.title_label.setFont(font) | |
self.title_label.setStyleSheet("border: 0px solid blue;") | |
self.title_bar_layout.addWidget(self.title_label) | |
self.title_bar_layout.addStretch() | |
self.title_bar_layout.addWidget(self.btn_minimize) | |
self.title_bar_layout.addWidget(self.btn_close) | |
# Create a spacer item with expanding policy | |
spacer = QSpacerItem(5, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) | |
self.title_bar_layout.addSpacerItem(spacer) # Add spacer to the layout | |
layout.addWidget(self.title_bar) | |
self.drawing_widget = DrawingWidget(self) | |
layout.addWidget(self.drawing_widget) | |
self.layout = layout | |
self.setLayout(layout) | |
# Add keyboard shortcuts | |
self.shortcut_screenshot = QShortcut(QKeySequence("Ctrl+1"), self) | |
self.shortcut_screenshot.activated.connect( | |
lambda: self.button_handler.just_screenshot() | |
) | |
self.shortcut_screenshot = QShortcut(QKeySequence("Ctrl+2"), self) | |
self.shortcut_screenshot.activated.connect( | |
lambda: self.button_handler.toggle_recording(take_system_audio=True) | |
) | |
self.shortcut_no_screenshot = QShortcut(QKeySequence("Ctrl+e"), self) | |
self.shortcut_no_screenshot.activated.connect( | |
lambda: self.button_handler.toggle_recording(take_system_audio=True) | |
) | |
self.shortcut_no_screenshot = QShortcut(QKeySequence("Ctrl+3"), self) | |
self.shortcut_no_screenshot.activated.connect( | |
lambda: self.button_handler.toggle_recording(no_screenshot=True) | |
) | |
# I want to create an input box to bottom left and a send button to bottom right | |
input_box = CustomTextEdit(self) | |
self.input_box = input_box | |
input_box.setFixedHeight(80) | |
# Set text wrapping. I dont wat to cut the text | |
input_box.setWordWrapMode(QtGui.QTextOption.NoWrap) | |
# Change the font size | |
font = QtGui.QFont() | |
font.setPointSize(12) | |
input_box.setFont(font) | |
self.highlighter = PythonSyntaxHighlighter(self.input_box.document()) | |
if load_api_key() == "CHANGE_ME": | |
input_box.setPlaceholderText("Save your API Key, go to settings") | |
else: | |
if platform.system() == "Darwin": | |
if llm_settings[load_model_settings()]["vision"] is False: | |
input_box.setPlaceholderText("Type here \nsand ↵ ") | |
else: | |
input_box.setPlaceholderText( | |
"Type here \nand ↵ \nor ⌘ + ↵ (+screenshot)" | |
) | |
else: | |
if llm_settings[load_model_settings()]["vision"] is False: | |
input_box.setPlaceholderText("Type here \nand ↵ ") | |
else: | |
input_box.setPlaceholderText( | |
"Type here \nand ↵ \nor Ctrl + ↵ (+screenshot)" | |
) | |
# Add an information and use enter icon to the input box for mac | |
input_box.setGeometry(30, self.height() - 60, 200, 80) | |
global the_input_box | |
the_input_box = input_box | |
def input_box_send(): | |
if input_box.toPlainText() != "": | |
click_sound() | |
self.button_handler.input_text(input_box.toPlainText()) | |
def input_box_send_screenshot(): | |
if input_box.toPlainText() != "": | |
click_sound() | |
self.button_handler.input_text_screenshot(input_box.toPlainText()) | |
self.layout.addWidget(input_box) | |
self.shortcut_enter = QShortcut(QKeySequence("Ctrl+Return"), self) | |
self.shortcut_enter.activated.connect(input_box_send_screenshot) | |
global return_key_event | |
return_key_event = input_box_send | |
button_layout_ = QHBoxLayout() | |
self.settingsButton = QPushButton("Chat Settings", self) | |
self.settingsButton.clicked.connect(settings_popup) | |
self.llmsettingsButton = QPushButton("LLM Settings", self) | |
self.llmsettingsButton.clicked.connect(llmsettings_popup) | |
button_layout_.addWidget(self.settingsButton) | |
button_layout_.addWidget(self.llmsettingsButton) | |
self.layout.addLayout(button_layout_) | |
self.worker = Worker() | |
self.worker.text_to_set.connect(self.set_text) | |
self.worker.start() | |
self.worker_2 = Worker_2() | |
self.worker_2.text_to_set.connect(self.start_border_animation) | |
self.worker_2.text_to_set_title_bar.connect(self.set_title_bar_text) | |
self.worker_2.start() | |
self.worker_3 = Worker_3() | |
self.worker_3.text_to_set.connect(self.general_styling) | |
self.worker_3.start() | |
self.worker_collapse = Worker_collapse() | |
self.worker_collapse.text_to_set.connect(self.collapse_gca) | |
self.worker_collapse.start() | |
self.worker_uncollapse = Worker_uncollapse() | |
self.worker_uncollapse.text_to_set.connect(self.uncollapse_gca) | |
self.worker_uncollapse.start() | |
self.worker_show_logo = Worker_show_logo() | |
self.worker_show_logo.text_to_set.connect(self.show_logo) | |
self.worker_show_logo.start() | |
self.worker_hide_logo = Worker_hide_logo() | |
self.worker_hide_logo.text_to_set.connect(self.hide_logo) | |
self.worker_hide_logo.start() | |
self.worker_activate_long_gca = Worker_activate_long_gca() | |
self.worker_activate_long_gca.text_to_set.connect(self.activate_long_gca) | |
self.worker_activate_long_gca.start() | |
self.worker_deactivate_long_gca = Worker_deactivate_long_gca() | |
self.worker_deactivate_long_gca.text_to_set.connect(self.deactivate_long_gca) | |
self.worker_deactivate_long_gca.start() | |
self.worker_tray_and_task_bar_logo = Worker_tray_and_task_bar_logo() | |
self.worker_tray_and_task_bar_logo.text_to_set.connect( | |
self.tray_and_task_bar_logo | |
) | |
self.worker_tray_and_task_bar_logo.start() | |
# print height and width | |
print(self.height(), self.width()) | |
self.show() | |
def set_text(self, text): | |
global the_input_box | |
vertical_scrollbar = the_input_box.verticalScrollBar() | |
scroll_value = vertical_scrollbar.value() | |
the_input_box.setPlainText(text) | |
vertical_scrollbar.setValue(scroll_value) | |
def set_title_bar_text(self, text): | |
self.title_label.setText(text) | |
def update_from_thread(self, text, system=True): | |
self.worker.make_animation = True | |
if system: | |
text = "System: " + text | |
print("Updating from thread", text) | |
self.worker.the_input_text = text | |
def read_part_task_generate_only(self): | |
if not is_just_text_model_active() and the_main_window.tts_available: | |
threads = {} | |
the_okey_parts = split_with_multiple_delimiters( | |
self.worker.the_input_text, ".?!:" | |
) | |
for each in the_okey_parts: | |
if the_main_window.stop_talking: | |
break | |
the_thread = threading.Thread(target=text_to_speech, args=(each,)) | |
threads[each] = the_thread | |
the_thread.start() | |
for each in threads.values(): | |
each.join() | |
self.reading_thread_2 = False | |
def read_part_task(self): | |
if not is_just_text_model_active() and the_main_window.tts_available: | |
threads = {} | |
the_okey_parts = split_with_multiple_delimiters( | |
self.worker.the_input_text, ".?!:" | |
) | |
will_read_parts = [] | |
for each in the_okey_parts: | |
if the_main_window.stop_talking: | |
break | |
if each not in readed_sentences: | |
will_read_parts.append(each) | |
readed_sentences.append(each) | |
the_thread = threading.Thread(target=text_to_speech, args=(each,)) | |
threads[each] = the_thread | |
the_thread.start() | |
for each in will_read_parts: | |
if the_main_window.stop_talking: | |
break | |
threads[each].join() | |
tts_if_you_can(each, not_threaded=True, bypass_other_settings=True) | |
self.reading_thread = False | |
def set_text_to_input_box(self, text): | |
global readed_sentences | |
self.worker.make_animation = False | |
if self.worker.the_input_text.startswith("System:") or self.complated_answer: | |
self.worker.the_input_text = "" | |
self.complated_answer = False | |
readed_sentences = [] | |
if text not in (">", "<>", ">\n", "<", "<\n"): | |
self.worker.the_input_text += text | |
if self.reading_thread is not True and len(self.worker.the_input_text) > 40: | |
self.reading_thread = True | |
threading.Thread(target=self.read_part_task).start() | |
if ( | |
self.reading_thread_2 is not True | |
and len(self.worker.the_input_text) > 250 | |
): | |
self.reading_thread_2 = True | |
threading.Thread(target=self.read_part_task_generate_only).start() | |
else: | |
print("Problem on text chars") | |
def set_text_from_api(self, text): | |
self.worker.make_animation = True | |
self.worker.the_input_text = text | |
def active_border_animation(self, title_bar_text=None): | |
if self.worker_2.title_bar_text is not None: | |
if self.worker_2.title_bar_text != title_bar_text: | |
return | |
self.worker_2.the_input_text = True | |
if title_bar_text is None: | |
title_bar_text = " " + name() | |
else: | |
title_bar_text = f" {title_bar_text}" | |
if len(title_bar_text) > 33: | |
title_bar_text = title_bar_text[:30] + "..." | |
self.worker_2.title_bar_text = title_bar_text | |
self.btn_minimize.hide() | |
self.btn_close.hide() | |
def deactive_border_animation(self, title_bar_text=None): | |
if title_bar_text is None: | |
title_bar_text = " " + name() | |
else: | |
title_bar_text = f" {title_bar_text}" | |
if len(title_bar_text) > 33: | |
title_bar_text = title_bar_text[:30] + "..." | |
if self.worker_2.title_bar_text is not None: | |
if self.worker_2.title_bar_text != title_bar_text: | |
return | |
self.worker_2.the_input_text = False | |
self.worker_2.title_bar_text = None | |
time.sleep(1) | |
self.btn_minimize.show() | |
self.btn_close.show() | |
def mouseMoveEvent(self, event: QMouseEvent): | |
delta = QPoint(event.globalPos() - self.old_position) | |
if event.buttons() == Qt.LeftButton and self.title_bar.underMouse(): | |
self.move(self.x() + delta.x(), self.y() + delta.y()) | |
self.old_position = event.globalPos() | |
def mousePressEvent(self, event: QMouseEvent): | |
self.old_position = event.globalPos() | |
def remove_screenshot_button(self): | |
self.update() | |
def add_screenshot_button(self): | |
self.update() | |
def update_state(self, new_state): | |
assistant_stopped = False | |
if self.state == "aitalking" and new_state == "idle": | |
assistant_stopped = True | |
if self.manuel_stop: | |
assistant_stopped = False | |
self.manuel_stop = False | |
self.state = new_state | |
print(f"State updated: {new_state}") | |
if "talking" in new_state: | |
self.tray.setIcon(self.tray_active_icon) | |
self.pulse_frame = 0 | |
if self.pulse_timer: | |
self.pulse_timer.stop() | |
self.pulse_timer = None | |
self.pulse_timer = QTimer(self) | |
self.pulse_timer.timeout.connect(self.pulse_circle) | |
self.pulse_timer.start(5) | |
elif new_state == "thinking": | |
the_main_window.update_from_thread("Thinking...") | |
self.pulse_frame = 0 | |
if self.pulse_timer: | |
self.pulse_timer.stop() | |
self.pulse_timer = None | |
self.pulse_timer = QTimer(self) | |
self.pulse_timer.timeout.connect(self.pulse_circle) | |
self.pulse_timer.start(20) | |
elif self.pulse_timer: | |
self.tray.setIcon(self.tray_icon) | |
self.pulse_timer.stop() | |
self.pulse_timer = None | |
self.update() # Trigger a repaint | |
if assistant_stopped: | |
global the_input_box | |
if ( | |
the_input_box.toPlainText().endswith("?") | |
and is_continuously_conversations_setting_active() | |
): | |
self.button_handler.toggle_recording( | |
no_screenshot=True, new_record=True | |
) | |
if new_state == "idle": | |
click_sound() | |
def pulse_circle(self): | |
self.pulse_frame = (self.pulse_frame + 1) % 100 | |
self.update() | |
def collapse_gca(self): | |
self.collapse = True | |
self.collapse_window() | |
activate_collapse_setting() | |
self.update_screen() | |
def collapse_gca_api(self): | |
self.worker_collapse.the_input_text = "True" | |
def uncollapse_gca(self): | |
self.collapse = False | |
print() | |
# hide all buttons and input box | |
the_input_box.show() | |
self.settingsButton.show() | |
self.llmsettingsButton.show() | |
deactivate_collapse_setting() | |
self.update_screen() | |
def uncollapse_gca_api(self): | |
self.worker_uncollapse.the_input_text = "True" | |
def show_logo(self): | |
self.the_image.setPixmap(QtGui.QPixmap(load_logo_file_path()).scaled(25, 25)) | |
self.the_image.show() | |
self.update_screen() | |
def tray_and_task_bar_logo(self): | |
app_icon = QtGui.QIcon() | |
app_icon.addFile(load_logo_file_path(), QtCore.QSize(48, 48)) | |
self.the_app.setWindowIcon(app_icon) | |
self.tray.setIcon(app_icon) | |
self.tray_icon = app_icon | |
self.tray_active_icon = app_icon | |
print("ICON Set", load_logo_file_path()) | |
def tray_and_task_bar_logo_api(self): | |
self.worker_tray_and_task_bar_logo.the_input_text = "True" | |
def show_logo_api(self): | |
self.worker_show_logo.the_input_text = "True" | |
def hide_logo(self): | |
self.the_image.hide() | |
self.update_screen() | |
def hide_logo_api(self): | |
self.worker_hide_logo.the_input_text = "True" | |
def activate_long_gca(self): | |
activate_long_gca_setting() | |
self.update_screen() | |
def activate_long_gca_api(self): | |
self.worker_activate_long_gca.the_input_text = "True" | |
def deactivate_long_gca(self): | |
deactivate_long_gca_setting() | |
self.update_screen() | |
def deactivate_long_gca_api(self): | |
self.worker_deactivate_long_gca.the_input_text = "True" | |
def update_screen(self): | |
width = 210 | |
height = 320 | |
if is_logo_active_setting_active(): | |
height += 35 | |
if is_collapse_setting_active(): | |
height = 150 | |
if is_logo_active_setting_active(): | |
height += 35 | |
if is_long_gca_setting_active(): | |
if not is_collapse_setting_active(): | |
height += 500 | |
self.input_box.setFixedHeight(580) | |
else: | |
self.input_box.setFixedHeight(80) | |
self.setFixedSize(width, height) | |
self.put_location() | |
def put_window_to_right_side_of_screen(self): | |
screen = QDesktopWidget().screenGeometry() | |
window = self.frameGeometry() | |
# Calculate x position for the right side of the screen and center vertically | |
x = screen.width() - window.width() # To right side | |
y = (screen.height() - window.height()) // 2 # Center vertically | |
# Add a small offset to the right side | |
x -= 10 | |
self.move(x, y) | |