i want to add a text browser

in the side of display_frame frame is displaying in the center

now i want to display in the right side


and left side will be a text browser



where 


the output of this streaming will be displayed

instead of     image_file_path = "dahwin.png" the current image or current state image which is displaying the window  display_frame


and it will continuously doning the same thing it will be analysis the current sate image

and displayiing in the text browser

it have to be stream in text browser tooi










import sys
import cv2
import time
import imutils
import mss
import pyautogui
from PIL import Image, ImageDraw, ImageFont
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QLabel, QVBoxLayout, 
    QWidget, QComboBox, QPushButton, QHBoxLayout, QFileDialog
)
import numpy as np
from datetime import datetime

class VideoPlayer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.WIDTH = 800
        self.cap = None
        self.fps = 0
        self.st = time.time()
        self.frames_to_count = 20
        self.cnt = 0
        self.current_source = None
        self.previous_source = None
        self.previous_camera_index = 0
        
        # For screen recording
        self.sct = mss.mss()
        self.monitor = self.sct.monitors[1]
        try:
            self.font = ImageFont.truetype("arial.ttf", 36)
        except:
            self.font = ImageFont.load_default()
        try:
            mouse_icon = Image.open('mouse_icon.png').convert('RGBA')
            icon_width, icon_height = 26, 26
            self.mouse_icon = mouse_icon.resize((icon_width, icon_height))
        except:
            print("Mouse icon not found, using default cursor")
            self.mouse_icon = None
        
        # For video recording
        self.is_recording = False
        self.out = None
        self.record_width = 1920
        self.record_height = 1080
        
        # Set the modern dark blue theme stylesheet
        self.setStyleSheet("""
            QMainWindow {
                background: #1E213E;
            }
            QLabel {
                color: #ffffff;
                font-family: 'Segoe UI', sans-serif;
                padding: 5px;
            }
            QPushButton {
                background: #4A5BF9;
                border: none;
                border-radius: 8px;
                color: white;
                padding: 10px 20px;
                font-size: 14px;
                font-weight: 500;
                min-width: 150px;
                margin: 5px;
            }
            QPushButton:hover {
                background: #5A6AFF;
            }
            QComboBox {
                background: rgba(74, 91, 249, 0.1);
                border: 2px solid #4A5BF9;
                border-radius: 6px;
                color: white;
                padding: 8px 15px;
                min-width: 150px;
                margin: 5px;
            }
            QComboBox QAbstractItemView {
                background-color: #262B4D;
                color: white;
                selection-background-color: #4A5BF9;
            }
            QComboBox::drop-down {
                border: none;
                background: #4A5BF9;
                width: 30px;
                border-top-right-radius: 4px;
                border-bottom-right-radius: 4px;
            }
            QComboBox::down-arrow {
                image: none;
            }
            QWidget {
                background: transparent;
            }
            QStatusBar {
                background: #262B4D;
                color: #ffffff;
            }
        """)
        
        self.setWindowTitle("Dahyun Vision")
        self.setup_ui()
        
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_frame)
        
        self.camera_count = self.count_cameras()
        self.populate_camera_list()

    def setup_ui(self):
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setSpacing(20)
        main_layout.setContentsMargins(30, 30, 30, 30)
        
        title_label = QLabel("Dahyun Vision")
        title_label.setStyleSheet("""
            font-size: 24px;
            color: #ffffff;
            font-weight: bold;
            padding: 15px;
            background: #262B4D;
            border-radius: 12px;
        """)
        title_label.setAlignment(Qt.AlignCenter)
        main_layout.addWidget(title_label)
        
        # Add FPS Label
        self.fps_label = QLabel("FPS: 0")
        self.fps_label.setStyleSheet("""
            font-size: 18px;
            color: #4A5BF9;
            font-weight: bold;
            padding: 10px;
            background: rgba(74, 91, 249, 0.1);
            border-radius: 8px;
        """)
        self.fps_label.setAlignment(Qt.AlignCenter)
        main_layout.addWidget(self.fps_label)
        
        controls_container = QWidget()
        controls_container.setStyleSheet("""
            QWidget {
                background: #262B4D;
                border-radius: 15px;
                padding: 20px;
            }
        """)
        controls_layout = QHBoxLayout(controls_container)
        
        camera_layout = QVBoxLayout()
        self.camera_combo = QComboBox()
        self.camera_combo.currentIndexChanged.connect(self.source_changed)
        self.camera_button = QPushButton("▶ Start Camera/Screen")
        self.camera_button.clicked.connect(self.toggle_camera_or_screen)
        camera_layout.addWidget(QLabel("Select Camera or Screen:"))
        camera_layout.addWidget(self.camera_combo)
        camera_layout.addWidget(self.camera_button)
        
        video_layout = QVBoxLayout()
        self.video_button = QPushButton("📁 Select Video")
        self.video_button.clicked.connect(self.select_video_file)
        video_layout.addWidget(QLabel("Video File:"))
        video_layout.addWidget(self.video_button)
        
        record_layout = QVBoxLayout()
        self.record_button = QPushButton("🔴 Start Recording")
        self.record_button.clicked.connect(self.toggle_recording)
        record_layout.addWidget(QLabel("Recording:"))
        record_layout.addWidget(self.record_button)
        
        controls_layout.addLayout(camera_layout)
        controls_layout.addLayout(video_layout)
        controls_layout.addLayout(record_layout)
        main_layout.addWidget(controls_container)
        
        self.video_label = QLabel()
        self.video_label.setAlignment(Qt.AlignCenter)
        self.video_label.setStyleSheet("""
            QLabel {
                background: #262B4D;
                border-radius: 15px;
                padding: 20px;
                border: 2px solid rgba(74, 91, 249, 0.3);
            }
        """)
        self.video_label.setText("No video source selected")
        main_layout.addWidget(self.video_label)
        
        self.resize(self.WIDTH, 600)

    def calculate_fps(self):
        self.cnt += 1
        if self.cnt == self.frames_to_count:
            try:
                self.fps = round(self.frames_to_count / (time.time() - self.st))
                self.st = time.time()
                self.cnt = 0
                self.fps_label.setText(f"FPS: {self.fps}")
            except:
                pass

    def count_cameras(self):
        camera_count = 0
        for i in range(100000):
            cap = cv2.VideoCapture(i)
            if cap.isOpened():
                camera_count += 1
                cap.release()
            else:
                break
        return camera_count


    def populate_camera_list(self):
        self.camera_combo.clear()
        for i in range(self.camera_count):
            self.camera_combo.addItem(f"Camera {i}")
        self.camera_combo.addItem("See Screen")

    def source_changed(self, index):
        if self.current_source == 'file':
            # If a video is playing, changing the camera source should stop the video
            self.stop_video()
        
        if index == self.camera_count:
            self.switch_to_screen_recording()
        else:
            self.switch_to_camera(index)

    def toggle_camera_or_screen(self):
        if self.current_source in ['camera', 'screen']:
            self.stop_camera_or_screen()
        else:
            if self.current_source == 'file':
                self.stop_video()
            self.start_camera_or_screen()

    def start_camera_or_screen(self):
        current_index = self.camera_combo.currentIndex()
        if current_index == self.camera_count:
            self.switch_to_screen_recording()
        else:
            self.switch_to_camera(current_index)

    def stop_camera_or_screen(self):
        self.timer.stop()
        if self.cap is not None:
            self.cap.release()
            self.cap = None
        self.current_source = None
        self.video_label.setText("No video source selected")
        self.camera_button.setText("▶ Start Camera/Screen")
        self.fps_label.setText("FPS: 0")

    def switch_to_camera(self, camera_index):
        if self.cap is not None:
            self.cap.release()
        
        self.cap = cv2.VideoCapture(camera_index)
        if self.cap.isOpened():
            self.timer.start(30)
            self.current_source = 'camera'
            self.previous_source = 'camera'
            self.previous_camera_index = camera_index
            self.camera_button.setText("⏹ Stop Camera/Screen")
            # Reset FPS counter
            self.cnt = 0
            self.st = time.time()
            self.fps = 0
        else:
            self.video_label.setText("Failed to access camera.")
            self.current_source = None

    def switch_to_screen_recording(self):
        if self.cap is not None and self.cap.isOpened():
            self.cap.release()
            
        self.cap = None  # No cv2.VideoCapture for screen
        self.timer.start(30)
        self.current_source = 'screen'
        self.previous_source = 'screen'
        self.previous_camera_index = None
        self.camera_button.setText("⏹ Stop Camera/Screen")
        # Reset FPS counter
        self.cnt = 0
        self.st = time.time()
        self.fps = 0

    def select_video_file(self):
        file_name, _ = QFileDialog.getOpenFileName(
            self, "Select Video File", "",
            "Video Files (*.mp4 *.avi *.mkv *.mov);;All Files (*.*)"
        )
        if file_name:
            if self.current_source in ['camera', 'screen']:
                self.stop_camera_or_screen()
            else:
                self.stop_video()

            self.cap = cv2.VideoCapture(file_name)
            if self.cap.isOpened():
                self.timer.start(30)
                self.current_source = 'file'
                # Reset FPS counter
                self.cnt = 0
                self.st = time.time()
                self.fps = 0
                self.video_label.setText("Playing video...")
                self.camera_button.setText("▶ Start Camera/Screen")
            else:
                self.video_label.setText("Failed to open video file.")
                self.current_source = None

    def stop_video(self):
        self.timer.stop()
        if self.cap is not None:
            self.cap.release()
            self.cap = None

        if self.current_source == 'file' and self.previous_source:
            if self.previous_source == 'screen':
                self.camera_combo.setCurrentIndex(self.camera_count)
                self.switch_to_screen_recording()
            elif self.previous_source == 'camera' and self.previous_camera_index is not None:
                self.camera_combo.setCurrentIndex(self.previous_camera_index)
                self.switch_to_camera(self.previous_camera_index)
        else:
            self.video_label.setText("No video source selected")
            self.current_source = None
            self.camera_button.setText("▶ Start Camera/Screen")
            self.fps_label.setText("FPS: 0")

    def update_frame(self):
        frame = None
        if self.current_source == 'file' or self.current_source == 'camera':
            ret, frame = self.cap.read()
            if ret:
                self.calculate_fps()
                self.display_frame(frame)
            else:
                self.stop_video()
        elif self.current_source == 'screen':
            screen_image = self.sct.grab(self.monitor)
            img = Image.frombytes("RGB", (screen_image.width, screen_image.height), screen_image.rgb)
            draw = ImageDraw.Draw(img)
            mouse_x, mouse_y = pyautogui.position()
            if self.mouse_icon:
                img.paste(self.mouse_icon, (mouse_x, mouse_y), self.mouse_icon)
            frame = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
            self.calculate_fps()
            self.display_frame(frame)

        if self.is_recording and frame is not None:
            frame_resized = cv2.resize(frame, (self.record_width, self.record_height))
            self.out.write(frame_resized)

    def display_frame(self, frame):
        frame = imutils.resize(frame, width=self.WIDTH)
        img = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_BGR888)
        pix = QPixmap.fromImage(img)
        self.video_label.setPixmap(pix)

    def toggle_recording(self):
        if not self.is_recording:
            self.start_recording()
        else:
            self.stop_recording()

    def start_recording(self):
        if self.current_source is None:
            return

        # Generate the filename with the current date and time
        current_datetime = datetime.now().strftime("%Y%m%d_%H%M%S")
        default_filename = f"recording_{current_datetime}.mp4"
        
        file_name, _ = QFileDialog.getSaveFileName(self, "Save Video", default_filename, "MP4 files (*.mp4)")
        if file_name:
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            self.out = cv2.VideoWriter(file_name, fourcc, 20.0, (self.record_width, self.record_height))
            self.is_recording = True
            self.record_button.setText("⏹ Stop Recording")
            self.record_button.setStyleSheet("background: #FF4136;")

    def stop_recording(self):
        if self.is_recording:
            self.is_recording = False
            self.out.release()
            self.record_button.setText("🔴 Start Recording")
            self.record_button.setStyleSheet("background: #4A5BF9;")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    player = VideoPlayer()
    player.show()
    sys.exit(app.exec())














i want to add a text browser

in the side of display_frame frame is displaying in the center

now i want to display in the right side


and left side will be a text browser



where 


the output of this streaming will be displayed

instead of     image_file_path = "dahwin.png" the current image or current state image which is displaying the window  display_frame


and it will continuously doning the same thing it will be analysis the current sate image

and displayiing in the text browser

it have to be stream in text browser tooi

import cv2
from PIL import Image
import httpx
from io import BytesIO
import asyncio
import nest_asyncio
nest_asyncio.apply()

async def send_request(prompt: str, image_path: str, server_url: str):
    """
    Sends a prompt and an image to the server and handles streaming response.
    
    :param prompt: The prompt string.
    :param image_path: Path to the image file.
    :param server_url: The server endpoint URL.
    """
    # Load and prepare the image
    image = cv2.imread(image_path)
    if image is None:
        print(f"Failed to load image from {image_path}")
        return
    
    # Convert BGR to RGB
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    image_pillow = Image.fromarray(image_rgb)
    
    # Convert to bytes
    buffered = BytesIO()
    image_pillow.save(buffered, format="PNG")
    image_bytes = buffered.getvalue()
    
    # Prepare multipart form data
    files = {
        'prompt': (None, prompt),
        'image': ('image.png', image_bytes, 'image/png')
    }
    
    async with httpx.AsyncClient() as client:
        try:
            async with client.stream('POST', server_url, files=files, timeout=None) as response:
                if response.status_code != 200:
                    print(f"Server returned status code {response.status_code}")
                    return
                
                async for chunk in response.aiter_text():
                    print(chunk, end="", flush=True)
                    
        except httpx.RequestError as e:
            print(f"Request error: {e}")
        except Exception as e:
            print(f"Unexpected error: {e}")

if __name__ == "__main__":
    prompt_text = "describe this image or current state"
    image_file_path = "dahwin.png"
    base = "https://33d0-34-81-38-3.ngrok-free.app"
    server_endpoint = f"{base}/process"
    
    asyncio.run(send_request(prompt_text, image_file_path, server_endpoint))












write the full updated intergrated code

you have to write the full a to z code