Jak zbudować okno chatu?

0

Cześć
Proszę o pomoc, próbuje zbudować aplikację desktopową, która docelowo ma być czatem zintegrowanym z gpt 3.5 (po przez Azure OpenAI Service). Sporo udało mi się zrobić ale mam następujące "wyzwania".
Aplikacja ma wyglądać jak typowy chat, tzn. okienko jest o wymiarze 350x600px, na dole jest pole tekstowe do wpisywania zapytań przez usera i jest część nad polem tekstowym na którym zapisuje się cała historia konwersacji.

screenshot-20240205005853.png
I teraz problem number one.
Nie potrafię zrobić pola tekstowego (textbox, label), którego wysokość będzie się automatycznie dostosowywać do ilości tekstu. Jak widać powyżej pytanie użytkownika wklejam w fioletowego textboxa i po prawej stronie pojawia się (biały) scroll. Mogę go oczywiście wyłączyć, ale nie o to chodzi - jak zrobić jakieś pole, którego wysokość będzie dynamiczna, a szerokość stała (stałą szerokość można ustawić więc to nie problem)

Problem number two:
Cała sekcja w której zapisuje się historia rozmowy (po prawej stronie pytania usera, po lewej odpowiedź gen ai) jest umieszczona w ramce. Wszystko ładnie działa, cała historia rozmowy buduje się od dołu ale ramka jest statyczna, nie ma scrolla. Jeżeli zamiast zwykłej ramki użyje CTkScrollableFrame to scroll się tworzy ale wszystkie elementy (historia rozmowy) dodają się od góry, a nie od dołu (nawet jeżeli użyje (...).pack(side="bottom"). Zatem pytanie jest takie - jak ustawić CTkScrollableFrame aby kolejne elementy tej ramce były dodawana na jej dole i aby scroll działał poprawnie?

Korzystam z python 3.9.1, tkinter, customtkinter

Z góry dziękuje za wskazówki, linki do materiał, za jakąkolwiek pomoc.
Pozdrawiam

1

Coś więcej, jakiś kod? Podejrzewam że odpowiedź na pytanie: "jak zrobić pole którego wysokość jest dynamiczna" jest w dokumentacji...

0

Nie potrafię zrobić pola tekstowego (textbox, label), którego wysokość będzie się automatycznie dostosowywać do ilości tekstu

Utwórz pole tekstowe z opcją wrap="word".
Wysokość pola tekstowego obliczaj na podstawie ilość linijek tekstu. Możesz do tego użyć wyrażenie .count("\n")

0
lion137 napisał(a):

Coś więcej, jakiś kod? Podejrzewam że odpowiedź na pytanie: "jak zrobić pole którego wysokość jest dynamiczna" jest w dokumentacji...

import customtkinter as ctk

window = ctk.CTk()
window.geometry("350x600")

def content():
    question = entry.get()
    # entry.delete("1.0", ctk.END)
    response.insert(ctk.END, question)
    entry.delete("0", ctk.END)

main_frame = ctk.CTkFrame(
    window,
    width=350,
    height=600,
)
main_frame.pack(side="bottom")

entry = ctk.CTkEntry(
    main_frame,
    width=340,
    placeholder_text="Napisz coś...",
    text_color="#FFCC70",
    fg_color="#000000"
)
entry.bind("<Return>", lambda event: content())
entry.pack(side="bottom")

response = ctk.CTkTextbox(
    main_frame,
    width=340,
    height=45,
    wrap="word",
    text_color="#000000",
    fg_color="#cccccc",
)
response.pack(side="bottom")

window.mainloop()

Chciałbym aby wysokość pola ctk.CTKTextBox dostosowywała się automatycznie to ilości tesktu jaki otrzymuje z pola entry.
Z dokumentacji customtkinter wynika, że mogę ustawić konkretną wysokość oraz uruchomić scrolla w ramach textboxa. Pytanie czy można to jakoś obejść aby wysokość dostosowywała się automatycznie.

Ponadto, jeżeli zamiast ctk.CTkFrame zastosuje ctk.CTkScrollableFrame to w ramce pojawia się scroll i całość zawartości ramki leci na samą górę, a ja bym chciał aby zawartość ramki pozostała na dole.

sherman napisał(a):

Nie potrafię zrobić pola tekstowego (textbox, label), którego wysokość będzie się automatycznie dostosowywać do ilości tekstu

Utwórz pole tekstowe z opcją wrap="word".
Wysokość pola tekstowego obliczaj na podstawie ilość linijek tekstu. Możesz do tego użyć wyrażenie .count("\n")

O, brzmi ciekawie - sprawdzę! Dzięki!

0

Nie wiem, albo to nie działa, albo robię coś źle.
Dodałem taką funkcję:

def count_lines():
    content_text = response.get("1.0", ctk.END)
    line_count = content_text.count("\n")
    print(f"Liczba wierszy: {line_count}")

Wynik tej funkcji to zawsze 1.

0
content_text = input_text.get("1.0", "end-1c")
line_count = content_text.count("\n") + 1
print(f"Liczba wierszy: {line_count}")

label.config(height=line_count)

0

Dalej nie działa - być może robię coś źle.. wg mnie ten tekst w polu tekstowym nie jest połamany znakiem "\n".
Jeżeli mam pole tekstowe:
screenshot-20240206212912.png

które jak widać powyżej ma dwa wiersze tekstu i teraz puszczę taki kod:

content_text = input_text.get("1.0", "end-1c")
print(content_text)

to wynik jest następujący:
screenshot-20240206213105.png
tekst nie jest połamany, to ciąg znaków który nie jest ograniczony "zawijaniem".

Powoli tracę nadzieje i chyba będę musiał zmienić koncepcje i zrobić całego czata na textoboxie.. ale wyglądać to będzie słabo..

0

Dla potomnych - problem z automatyczną wysokością textboxa rozwiązałem inaczej. Może mało elegancko ale działa i nawet daje efekt pseudo animacji.
Po utworzeniu pola tekstowego i zainsertowaniu do niego tekstu sprawdzam pozycję scrolla. Jeżeli pozycja jest poniżej wartości 1.0 tzn. że skroll istnieje i w pętli dodaje 15px do wysokości textboxa. I tak do momentu aż wartość scrolla będzie równa 1.0.

height_value = 20

scroll = text_answer.yview()

while scroll[1] < 1.0:
  height_value += 15
  text_answer.configure(height = height_value)
  text_answer.yview_moveto(0.0)
  text_answer.update()
  scroll = text_answer.yview()

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.