diff --git a/.env b/.env new file mode 100644 index 0000000..945726b --- /dev/null +++ b/.env @@ -0,0 +1 @@ +GEMINI_API_KEY=YOUR_API_KEY_HERE diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7275bb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +venv/ diff --git a/Python UI/Pico_crosspoint-xp3a-mini-rpi-red.py b/Python UI/Pico_crosspoint-xp3a-mini-rpi-red.py index bf3e3ef..9c2fab1 100644 --- a/Python UI/Pico_crosspoint-xp3a-mini-rpi-red.py +++ b/Python UI/Pico_crosspoint-xp3a-mini-rpi-red.py @@ -17,6 +17,17 @@ # from tkinter import scrolledtext # +try: + import google.generativeai as genai + from dotenv import load_dotenv + import os + load_dotenv() + GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") + genai.configure(api_key=GEMINI_API_KEY) + GeminiModel = genai.GenerativeModel("gemini-3.1-flash-lite-preview") +except: + showwarning("WARNING","google-generativeai or python-dotenv not installed? Run: pip install google-generativeai python-dotenv") +# # adjust for your specific hardware by changing these values in the alice.init file ADC_Cal = 3.25 VOpenCircuit = 2.4 @@ -1644,6 +1655,9 @@ def MakeBreadboardScreen(): global CompSpinBoxList_RC, CompSpinBoxList_TL, CompSpinBoxList_BL, CompSpinBoxList_TR, CompSpinBoxList_BR global click_loc, breadboard_image, JPcolors, breadboard_canvas + style = Style() # This accesses the ttk Style object + style.configure("Prompt.TEntry", fieldbackground="white", foreground="black") + if BreadboardStatus.get() == 0: try: XlabLogo_image = PhotoImage(file='./XLab-logo.png') # @@ -1826,10 +1840,80 @@ def MakeBreadboardScreen(): VerifyButton.grid(row=20, column=0, columnspan=2, sticky=W, pady=1) PassFailSvBB = Label(matrixwindow,text="") PassFailSvBB.grid(row=20, column=2, columnspan=4, sticky=W, pady=1) - + + if HWRevOne == "Red3": TestResButton = Button(matrixwindow, text="Man Test Resistor", style="W17.TButton", command=MakeTestResWindow) TestResButton.grid(row=21, column=0, columnspan=2, sticky=W, pady=1) + + + # --- PROMPT BOX --- + # --- CLASSIC TK SETUP --- + import tkinter as tk_base # Ensure we have access to classic widgets + + # 1. CREATE STYLE FOR CURSOR + # This fixes the cursor color for the ttk Entry + style = Style() + style.configure("Prompt.TEntry", + fieldbackground="white", + foreground="black", + insertcolor="black", # <--- FIXES VISIBLE CURSOR + insertwidth=2) + + # 2. CREATE DRAGGABLE PANED WINDOW + # This allows you to drag the divider up/down to resize the chat + chat_paner = tk_base.PanedWindow(matrixwindow, + orient=tk_base.VERTICAL, + sashwidth=6, + sashrelief=tk_base.RAISED, + bg=FrameBG) + chat_paner.grid(row=22, column=0, columnspan=4, sticky="nsew", padx=5, pady=10) + + # Give row 22 all the weight so it fills the bottom area + matrixwindow.rowconfigure(22, weight=1) + + # 3. CHAT HISTORY (Top Pane) + global ChatHistory + ChatHistory = scrolledtext.ScrolledText( + chat_paner, # Parent is the paner + height=10, + state='disabled', + wrap='word', + bg="white", + foreground="black", + insertbackground="black", + font=("Arial", 10) + ) + + # Add to paned window + chat_paner.add(ChatHistory, minsize=100) + + # 4. PROMPT INPUT AREA (Bottom Pane) + # We use a frame to hold the Label and Entry together in the bottom pane + input_container = tk_base.Frame(chat_paner, bg=FrameBG) + + PromptLabel = Label(input_container, text="User Prompt:", style="A12B.TLabel") + PromptLabel.pack(side=tk_base.TOP, anchor=tk_base.W, pady=(5, 0)) + + global PromptBox + # Using Style "Prompt.TEntry" defined above + PromptBox = Entry(input_container, style="Prompt.TEntry") + PromptBox.pack(side=tk_base.TOP, fill=tk_base.X, pady=(0, 10)) + + # Add frame to paned window + chat_paner.add(input_container, minsize=80) + + # 5. CONFIGURE TAGS + ChatHistory.tag_configure("user_tag", foreground="#0078d4", font=("Arial", 10, "bold")) + ChatHistory.tag_configure("ai_tag", foreground="#2b88d8", font=("Arial", 10, "bold")) + ChatHistory.tag_configure("text_tag", foreground="black", font=("Arial", 10)) + ChatHistory.tag_configure("status_tag", foreground="gray", font=("Arial", 10, "italic")) + + # 6. BINDINGS & FOCUS + PromptBox.bind("", handle_user_prompt) + PromptBox.focus_set() + + ############################## ### MIDDLE SIDE OF SCREEN #### #### The top part of the middle section is the simulated image of the breadboard @@ -1877,6 +1961,9 @@ def MakeBreadboardScreen(): J_Connections_Labels.append(J_connections) ############################## + + # Add this at the end of the function to set focus on startup + PromptBox.focus_set() # def BBCAresize(event): global breadboard_canvas, BBwidth, BBheight, CANVASwidthBB, CANVASheightBB @@ -7708,4 +7795,49 @@ def onResSchClick(event): SendByt = SendStr.encode('utf-8') ser.write(SendByt) # + +# create an AI prompt box +def handle_user_prompt(event): + global PromptBox, ChatHistory + user_input = PromptBox.get().strip() + +def handle_user_prompt(event): + global PromptBox, ChatHistory, GeminiModel + user_input = PromptBox.get().strip() + + if not user_input: + return + + # 1. Clear input immediately + PromptBox.delete(0, END) + + # 2. Enable box for appending + ChatHistory.config(state=NORMAL) + + # 3. Add User Prompt with Tag + ChatHistory.insert(END, "\nYou: ", "user_tag") + ChatHistory.insert(END, f"{user_input}\n", "text_tag") + + # 4. "Thinking" Indicator + ChatHistory.insert(END, "AI: Thinking...\n", "status_tag") + ChatHistory.see(END) + ChatHistory.update() + + # 5. Get Gemini Response + try: + response = GeminiModel.generate_content(user_input) + reply = response.text + except Exception as e: + reply = f"Error: {str(e)}" + + # 6. Replace "Thinking..." with the actual response + ChatHistory.delete("end-2l", "end-1c") + ChatHistory.insert(END, "AI: ", "ai_tag") + ChatHistory.insert(END, f"{reply}\n", "text_tag") + + # 7. Finalize view + ChatHistory.see(END) + ChatHistory.config(state=DISABLED) + # ENSURE CURSOR STAYS IN BOX + PromptBox.focus_set()