Spaces:
Runtime error
Runtime 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 | |
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 threading | |
import time | |
import random | |
import math | |
from PyQt5.QtWidgets import QApplication, 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 ( | |
QPushButton, | |
QLabel, | |
QHBoxLayout, | |
) | |
from PyQt5.QtCore import QPoint | |
from PyQt5.QtWidgets import QTextEdit | |
from PyQt5 import QtGui | |
from PyQt5.QtCore import QThread | |
print("Imported all libraries") | |
from PyQt5 import QtCore | |
try: | |
import ctypes | |
myappid = "onuratakan.gpt_computer_assistant.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() | |
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 | |
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 MainWindow.api_enabled 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 = " GPT Computer Assistant" | |
for i in range(len(the_text)): | |
self.text_to_set_title_bar.emit(the_text[:i + 1]) | |
self.msleep(10) | |
class DrawingWidget(QWidget): | |
def __init__(self, parent=None): | |
super(DrawingWidget, self).__init__(parent) | |
# Set widget properties if needed, e.g., size | |
self.main_ = parent | |
def paintEvent(self, event): | |
if not self.main_.should_paint: | |
return # Skip the drawing if should_paint is False | |
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.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 | |
painter.drawEllipse( | |
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)) # Green color with 2px thickness | |
# Draw the ellipse with the specified green border | |
painter.drawEllipse( | |
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)) | |
if self.main_.screen_available: | |
small_center_x = 165 | |
small_center_y = 25 | |
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_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 | |
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_top = 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_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 self.main_.collapse: | |
self.main_.small_circle_collapse_icon = QIcon(down_icon_path) | |
else: | |
self.main_.small_circle_collapse_icon = QIcon(up_icon_path) | |
self.main_.small_circle_collapse_icon.paint(painter, icon_rect) | |
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_.manuel_stop = True | |
self.main_.stop_talking = True | |
else: | |
if llm_settings[load_model_settings()]["vision"] is True: | |
self.main_.button_handler.toggle_recording(dont_save_image=True) | |
else: | |
self.main_.button_handler.toggle_recording(no_screenshot=True) | |
except: | |
pass | |
try: | |
if self.main_.small_circle_rect.contains(event.pos()): | |
if self.main_.state == "aitalking": | |
self.main_.manuel_stop = True | |
self.main_.stop_talking = True | |
else: | |
self.main_.button_handler.toggle_recording(no_screenshot=True) | |
except: | |
pass | |
try: | |
if self.main_.small_circle_left.contains(event.pos()): | |
if self.main_.state == "aitalking": | |
self.main_.manuel_stop = True | |
self.main_.stop_talking = True | |
else: | |
self.main_.button_handler.toggle_recording(take_system_audio=True) | |
except: | |
pass | |
try: | |
if self.main_.small_circle_left_top.contains(event.pos()): | |
if self.main_.state == "aitalking": | |
self.main_.manuel_stop = True | |
self.main_.stop_talking = True | |
else: | |
self.main_.button_handler.just_screenshot() | |
except: | |
pass | |
try: | |
if self.main_.small_circle_collapse.contains(event.pos()): | |
if self.main_.collapse: | |
self.main_.collapse = False | |
print() | |
# hide all buttons and input box | |
the_input_box.show() | |
if llm_settings[load_model_settings()]["vision"]: | |
self.main_.screenshot_button.show() | |
self.main_.settingsButton.show() | |
self.main_.llmsettingsButton.show() | |
self.main_.send_button.show() | |
self.main_.window().setFixedSize(self.main_.first_width, self.main_.first_height) | |
deactivate_collapse_setting() | |
else: | |
self.main_.collapse = True | |
self.main_.collapse_window() | |
activate_collapse_setting() | |
self.main_.update() | |
except: | |
pass | |
from PyQt5.QtCore import QVariantAnimation | |
class MainWindow(QMainWindow): | |
api_enabled = False | |
def __init__(self): | |
super().__init__() | |
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-assistant[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.should_paint = False # In order to initialize the painting, it will be overwritten by the settings | |
self.state = "idle" | |
self.pulse_timer = None | |
self.button_handler = ButtonHandler(self) | |
self.initUI() | |
self.old_position = self.pos() | |
if llm_settings[load_model_settings()]["transcription"]: | |
self.should_paint = True # Flag to control painting | |
else: | |
self.should_paint = False | |
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 | |
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): | |
self.setAttribute(Qt.WA_TranslucentBackground) | |
self.setStyleSheet("border-radius: 10px; background-color: rgba(45, 45, 45, 250);") | |
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.send_button_style = "border-radius: 5px; height: 25px; border-style: solid;" | |
self.screenshot_button_style = "border-radius: 5px; height: 25px; border-style: solid;" | |
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 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.send_button.setStyleSheet(self.send_button_style+"background-color: #2E2E2E; color: white;") | |
self.screenshot_button.setStyleSheet(self.screenshot_button_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.send_button.setStyleSheet(self.send_button_style+"background-color: #FFFFFF; color: black; ") | |
self.screenshot_button.setStyleSheet(self.screenshot_button_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.screenshot_button.hide() | |
self.settingsButton.hide() | |
self.llmsettingsButton.hide() | |
self.send_button.hide() | |
self.window().setFixedSize(self.width(), 140) | |
def initUI(self): | |
self.setWindowTitle("GPT") | |
self.setGeometry(100, 100, 200, 200) | |
self.setFixedSize(self.width()+10, self.height() + 80) | |
self.first_height = self.height() | |
self.first_width = self.width() | |
app_icon = QtGui.QIcon() | |
app_icon.addFile(icon_16_path, QtCore.QSize(16, 16)) | |
app_icon.addFile(icon_24_path, QtCore.QSize(24, 24)) | |
app_icon.addFile(icon_32_path, QtCore.QSize(32, 32)) | |
app_icon.addFile(icon_48_path, QtCore.QSize(48, 48)) | |
app_icon.addFile(icon_256_path, QtCore.QSize(256, 256)) | |
self.setWindowIcon(app_icon) | |
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;") | |
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(25, 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("X", self.title_bar) | |
self.btn_close.setFixedSize(30, 20) | |
self.btn_close.clicked.connect(stop_app) | |
self.title_label = QLabel(" GPT Computer Assistant", self.title_bar) | |
self.title_label.setStyleSheet("border: 0px solid blue;") | |
self.title_bar_layout.addWidget(self.title_label) | |
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(40) | |
if load_api_key() == "CHANGE_ME": | |
input_box.setPlaceholderText("Save your API Key, go to settings") | |
else: | |
input_box.setPlaceholderText("Type here") | |
input_box.setGeometry(30, self.height() - 60, 200, 30) | |
global the_input_box | |
the_input_box = input_box | |
def input_box_send(): | |
if input_box.toPlainText() != "": | |
self.button_handler.input_text(input_box.toPlainText()) | |
def input_box_send_screenshot(): | |
if input_box.toPlainText() != "": | |
self.button_handler.input_text_screenshot(input_box.toPlainText()) | |
self.layout.addWidget(input_box) | |
# Create a horizontal layout | |
button_layout = QHBoxLayout() | |
# Create the send button | |
self.send_button = QPushButton("Send", self) | |
self.send_button.clicked.connect(input_box_send) | |
# Create the screenshot button | |
self.screenshot_button = QPushButton("+Screenshot", self) | |
self.screenshot_button.clicked.connect(input_box_send_screenshot) | |
if llm_settings[load_model_settings()]["vision"] is False: | |
self.screenshot_button.hide() | |
# Add the buttons to the horizontal layout | |
button_layout.addWidget(self.send_button) | |
button_layout.addWidget(self.screenshot_button) | |
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 | |
self.layout.addLayout(button_layout) | |
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() | |
# 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 not the_main_window.api_enabled: | |
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 not the_main_window.api_enabled: | |
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() | |
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 = " GPT Computer Assistant" | |
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 = " GPT Computer Assistant" | |
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_painting(self): | |
self.should_paint = False # Set the flag to False | |
self.update() # Request a repaint, which will now skip drawing | |
def activate_painting(self): | |
self.should_paint = True | |
self.update() | |
def remove_screenshot_button(self): | |
self.screenshot_button.hide() | |
def add_screenshot_button(self): | |
self.screenshot_button.show() | |
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.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.pulse_timer.stop() | |
self.pulse_timer = None | |
self.update() # Trigger a repaint | |
if assistant_stopped: | |
if llm_settings[load_model_settings()]["transcription"]: | |
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) | |
def pulse_circle(self): | |
self.pulse_frame = (self.pulse_frame + 1) % 100 | |
self.update() | |