We made an app named "Arch App", here is the full code for it: #gui.py : # src/gui.py
from PyQt6 import QtWidgets, QtGui, QtCore
from image_fetcher import fetch_image_from_api, fetch_image_from_folder
#from llava_integration import generate_description, generate_extended_description
from utils import get_average_color
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QTextEdit
from PyQt6.QtGui import QPixmap, QFont, QLinearGradient
import sys
import os

class ArchitectApp(QWidget):
def init(self):
super().init()
self.setWindowTitle("HX - Architectural Showcase")
self.setStyleSheet("background-color: #121212;") # Dark mode
self.setGeometry(100, 100, 1200, 800)
#self.setWindowFlags(QtCore.Qt.WindowType.FramelessWindowHint)
#self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground)


    self.load_custom_font()
    self.init_ui()
    self.load_new_image()

def load_custom_font(self):
    font_id = QtGui.QFontDatabase.addApplicationFont(os.path.join('assets', 'fonts', 'HX-Sans.ttf'))
    if font_id != -1:
        family = QtGui.QFontDatabase.applicationFontFamilies(font_id)[0]
        self.custom_font = QFont(family, 12)
    else:
        self.custom_font = self.font()

def init_ui(self):
    main_layout = QHBoxLayout()
    self.setLayout(main_layout)
    
    # Left Widget
    self.left_widget = QWidget()
    self.left_layout = QVBoxLayout()
    self.left_widget.setLayout(self.left_layout)
    self.left_widget.setStyleSheet("background-color: #000000;")  # Black background
    
    self.image_label = QLabel()
    self.image_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
    self.image_label.setStyleSheet("border-radius: 15px;")
    self.left_layout.addWidget(self.image_label)

    # Set size policy for the left layout
    self.left_layout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetFixedSize)
    
    self.refresh_button = QPushButton("Refresh")
    self.refresh_button.setFont(self.custom_font)
    self.refresh_button.setStyleSheet("""
        QPushButton {
            background-color: #FFFFFF;
            color: #000000;
            border-radius: 10px;
            padding: 10px;
        }
        QPushButton:hover {
            background-color: #E0E0E0;
        }
    """)
    self.refresh_button.clicked.connect(self.load_new_image)
    self.left_layout.addWidget(self.refresh_button)
    
    # Right Widget
    self.right_widget = QWidget()
    self.right_layout = QVBoxLayout()
    self.right_widget.setLayout(self.right_layout)
    self.right_widget.setStyleSheet("background-color: #1E1E1E; border-radius: 15px;")  # Default dark
    
    # Top Section
    self.description_text = QTextEdit()
    self.description_text.setReadOnly(True)
    self.description_text.setFont(self.custom_font)
    self.description_text.setStyleSheet("background-color: transparent; color: #FFFFFF;")
    self.right_layout.addWidget(self.description_text)
    
    self.explain_button = QPushButton("Explain More")
    self.explain_button.setFont(self.custom_font)
    self.explain_button.setStyleSheet("""
        QPushButton {
            background-color: #FFFFFF;
            color: #000000;
            border-radius: 15px;
            padding: 10px 20px;
        }
        QPushButton:hover {
            background-color: #E0E0E0;
        }
    """)
    self.explain_button.clicked.connect(self.explain_more)
    self.right_layout.addWidget(self.explain_button)
    
    # Bottom Section
    self.bottom_section = QWidget()
    self.bottom_section.setStyleSheet("background-color: #121212; color: #FFFFFF;")
    self.bottom_layout = QHBoxLayout()
    self.bottom_section.setLayout(self.bottom_layout)
    
    self.image_link = QLabel("Image Link: Not Available")
    self.image_link.setFont(self.custom_font)
    self.image_link.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextBrowserInteraction)
    self.image_link.setOpenExternalLinks(True)
    
    self.additional_info = QLabel("Photographer: Unknown")
    self.additional_info.setFont(self.custom_font)
    
    self.bottom_layout.addWidget(self.image_link)
    self.bottom_layout.addWidget(self.additional_info)
    
    self.right_layout.addWidget(self.bottom_section)

    # Camera Info Label in the Bottom Section 
    self.camera_info = QLabel("Camera: N/A")
    self.camera_info.setFont(self.custom_font)
    self.bottom_layout.addWidget(self.camera_info)

    # Add a fullscreen button to the bottom section
    self.fullscreen_button = QPushButton("Fullscreen")
    self.fullscreen_button.setFont(self.custom_font)
    self.fullscreen_button.setStyleSheet("""
        QPushButton {
            background-color: #FFFFFF;
            color: #000000;
            border-radius: 10px;
            padding: 5px 10px; 
        }
        QPushButton:hover {
            background-color: #E0E0E0;
        }
    """)
    self.fullscreen_button.clicked.connect(self.toggle_fullscreen)
    self.bottom_layout.addWidget(self.fullscreen_button)  
    
    # Add widgets to main layout
    main_layout.addWidget(self.left_widget, 1)
    main_layout.addWidget(self.right_widget, 1)

def toggle_fullscreen(self):
    if self.image_label.isFullScreen():
        # Exit fullscreen mode for the image
        self.image_label.setParent(self.left_widget)  # Re-parent back to left_widget
        self.left_layout.addWidget(self.image_label)  # Add back to layout
        self.refresh_button.show()  # Show the refresh button
    else:
        # Enter fullscreen mode for the image
        self.image_label.setParent(None)  # Remove from current parent
        self.image_label.setWindowFlags(QtCore.Qt.WindowType.Window)  # Make it a top-level window
        self.image_label.showFullScreen() 
        self.refresh_button.hide() # Hide the refresh button

def load_new_image(self):
    # Fetch image (API or local)
    image_path, image_info = fetch_image_from_folder()  # Or fetch_image_from_folder()
    pixmap = QPixmap(image_path)
    self.image_label.setPixmap(pixmap.scaled(
        self.left_widget.size(), QtCore.Qt.AspectRatioMode.KeepAspectRatio, 
        QtCore.Qt.TransformationMode.SmoothTransformation))
    
    # Generate description
    description = "Testing the app" #generate_description(image_path)
    self.description_text.setText(description)

    # Update camera info
    self.camera_info.setText(f"Camera: {image_info['camera']}")
    
    # Update background color with gradient
    avg_color_top = get_average_color(image_path, sample_points=50, start_y=0.1) 
    avg_color_bottom = get_average_color(image_path, sample_points=50, start_y=0.6)

    gradient = QLinearGradient(QtCore.QPointF(0, 0), QtCore.QPointF(0, self.right_widget.height()))
    gradient.setColorAt(0.0, QtGui.QColor(avg_color_top))
    gradient.setColorAt(1.0, QtGui.QColor(avg_color_bottom))

    # Correcting the gradient application:
    self.right_widget.setStyleSheet(f"background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 {avg_color_top}, stop: 1 {avg_color_bottom}); border-radius: 15px;")

def explain_more(self):
    current_image_path, _ = fetch_image_from_folder()  # Replace with current image path
    extended_description = "Extended testing of the app!" #generate_extended_description(current_image_path)
    self.description_text.setText(extended_description)

def resizeEvent(self, event):
    # Ensure images resize properly
    if self.image_label.pixmap():
        self.image_label.setPixmap(self.image_label.pixmap().scaled(
            self.left_widget.size(), QtCore.Qt.AspectRatioMode.KeepAspectRatio,
            QtCore.Qt.TransformationMode.SmoothTransformation))
    super().resizeEvent(event)   ; #image_fetcher.py : # src/image_fetcher.py
import requests
import random
import os
from PIL import Image
from PIL.ExifTags import TAGS

UNSPLASH_ACCESS_KEY = 'YOUR_UNSPLASH_ACCESS_KEY'
UNSPLASH_API_URL = 'https://api.unsplash.com/photos/random'

def fetch_image_from_api(query='architecture'):
params = {
'query': query,
'client_id': UNSPLASH_ACCESS_KEY,
'orientation': 'landscape'
}
response = requests.get(UNSPLASH_API_URL, params=params)
data = response.json()
image_url = data['urls']['regular']
image_response = requests.get(image_url)
image_path = os.path.join('assets', 'images', 'current_image.jpg')
with open(image_path, 'wb') as f:
f.write(image_response.content)
image_info = {
'url': data['links']['html'],
'description': data.get('description') or data.get('alt_description'),
'photographer': data['user']['name']
}
return image_path, image_info

def fetch_image_from_folder(folder_path='assets/images'):
images = [img for img in os.listdir(folder_path) if img.lower().endswith(('.webp','.png', '.jpg', '.jpeg'))]
selected_image = random.choice(images)
image_path = os.path.join(folder_path, selected_image)


# Extract EXIF data
image_info = {
    'url': '',  # Local images might not have a URL
    'description': 'Local image description',
    'camera': 'N/A' 
}
try:
    with Image.open(image_path) as img:
        exif_data = img._getexif()
        if exif_data:
            exif_info = {
                TAGS.get(tag_id, tag_id): exif_data.get(tag_id)
                for tag_id in exif_data
                if TAGS.get(tag_id, tag_id) in ['Make', 'Model']
            }
            image_info['camera'] = f"{exif_info.get('Make', 'N/A')} {exif_info.get('Model', 'N/A')}"
except (IOError, AttributeError):
    pass # Handle cases where EXIF data can't be read 

return image_path, image_info ; #llava_integration.py :  ; #main.py : # src/main.py
import sys
from PyQt6.QtWidgets import QApplication
from gui import ArchitectApp

def main():
app = QApplication(sys.argv)
window = ArchitectApp()
window.show()
sys.exit(app.exec())

if name == "main":
main() ; #utils.py : # src/utils.py
from PIL import Image
import random

def get_average_color(image_path, sample_points=10, start_y=0):
image = Image.open(image_path)
image = image.resize((100, 100))
width, height = image.size
pixels = list(image.getdata())


# Calculate the starting y-coordinate for sampling
start_y = int(start_y * height)

step = (width * height) // sample_points  # Calculate step for even distribution
selected_pixels = [pixels[i * step] for i in range(sample_points)]

avg_rgb = tuple(sum(col) // len(col) for col in zip(*selected_pixels))
return f"rgb({avg_rgb[0]}, {avg_rgb[1]}, {avg_rgb[2]})"  ; #Project_structure.txt : architect_app/
├── assets/
│ ├── fonts/
│ │ └── HX_Sans.ttf
│ ├── images/
│ │ └── ... (local images)
├── src/
│ ├── main.py
│ ├── gui.py
│ ├── image_fetcher.py
│ ├── llava_integration.py
│ └── utils.py
├── requirements.txt
└── README.md