Improve this:
import gi
import os
import pickle
import requests
from fastai.vision.all import *
from io import BytesIO
from PIL import Image

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib

class ImagePredictorApp(Gtk.Window):
    def __init__(self):
        super().__init__(title="Image Predictor")
        self.set_border_width(10)
        self.set_default_size(400, 300)

        # Load model button
        self.load_model_button = Gtk.Button(label="Load Model (.pkl or .pth)")
        self.load_model_button.connect("clicked", self.on_load_model)
        
        # Image selection
        self.image_entry = Gtk.Entry()
        self.image_button = Gtk.Button(label="Select Image")
        self.image_button.connect("clicked", self.on_select_image)

        # Threshold slider
        self.threshold_adjustment = Gtk.Adjustment(50, 0, 100, 1, 10, 0)
        self.threshold_slider = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=self.threshold_adjustment)
        self.threshold_slider.set_digits(0)
        self.threshold_label = Gtk.Label(label=f"Prediction Threshold: {self.threshold_slider.get_value():.0f}%")
        self.threshold_slider.connect("value-changed", self.on_threshold_changed)

        # Predict button
        self.predict_button = Gtk.Button(label="Predict")
        self.predict_button.connect("clicked", self.on_predict)
        self.predict_button.set_sensitive(False)

        # Output label
        self.output_label = Gtk.Label(label="Output will be shown here.")

        # Layout
        grid = Gtk.Grid()
        grid.add(self.load_model_button)
        grid.attach_next_to(self.image_entry, self.load_model_button, Gtk.PositionType.BOTTOM, 1, 1)
        grid.attach_next_to(self.image_button, self.image_entry, Gtk.PositionType.RIGHT, 1, 1)
        grid.attach_next_to(self.threshold_label, self.image_entry, Gtk.PositionType.BOTTOM, 2, 1)
        grid.attach_next_to(self.threshold_slider, self.threshold_label, Gtk.PositionType.BOTTOM, 2, 1)
        grid.attach_next_to(self.predict_button, self.threshold_slider, Gtk.PositionType.BOTTOM, 2, 1)
        grid.attach_next_to(self.output_label, self.predict_button, Gtk.PositionType.BOTTOM, 2, 1)

        self.add(grid)

        self.model = None

    def on_load_model(self, button):
        dialog = Gtk.FileChooserDialog("Choose a .pkl or .pth file", self, 
                                        Gtk.FileChooserAction.OPEN)
        dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                           Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
        dialog.set_filter(self.create_file_filter())

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            model_path = dialog.get_filename()
            self.load_model(model_path)
        dialog.destroy()

    def create_file_filter(self):
        file_filter = Gtk.FileFilter()
        file_filter.set_name("Model files")
        file_filter.add_pattern("*.pkl")  # Allow .pkl files
        file_filter.add_pattern("*.pth")   # Allow .pth files
        return file_filter

    def load_model(self, model_path):
        with open(model_path, 'rb') as f:
            self.model = load_learner(f)
        self.predict_button.set_sensitive(True)

    def on_select_image(self, button):
        dialog = Gtk.FileChooserDialog("Choose an image", self, 
                                        Gtk.FileChooserAction.OPEN)
        dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                           Gtk.STOCK_OPEN, Gtk.ResponseType.OK)

        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            image_path = dialog.get_filename()
            self.image_entry.set_text(image_path)
        dialog.destroy()

    def on_threshold_changed(self, scale):
        value = self.threshold_slider.get_value()
        self.threshold_label.set_text(f"Prediction Threshold: {value:.0f}%")

    def on_predict(self, button):
        if self.model is None:
            self.output_label.set_text("Model not loaded.")
            return

        image_path = self.image_entry.get_text()

        # Check if the input is a URL
        if image_path.startswith(('http:', 'https:', 'ftp:')):
            try:
                response = requests.get(image_path)
                response.raise_for_status()
                image = PILImage.create(BytesIO(response.content))
            except requests.exceptions.RequestException as e:
                self.output_label.set_text(f"Error fetching image: {e}")
                return
        elif os.path.isfile(image_path):  # Local file case
            image = PILImage.create(image_path)
        else:
            self.output_label.set_text("Invalid image path.")
            return

        pred, pred_idx, outputs = self.model.predict(image)
        probabilities = outputs.numpy() * 100  # Convert to percentage
        classes = self.model.dls.vocab  # Get class names
        threshold = self.threshold_slider.get_value()  # Get the threshold value

        # Prepare formatted prediction results
        output_text = "Predictions:\n"
        predictions_to_print = []  # List to store predictions for terminal output
        for class_name, probability in zip(classes, probabilities):
            if probability >= threshold:
                output_text += f" - {class_name}: {probability:.2f}% (Confidence: {probability:.2f}%)\n"
                predictions_to_print.append(f"{class_name}: {probability:.2f}% (Confidence: {probability:.2f}%)")

        output_text += f"Overall: {pred}"

        # Print predictions in the terminal
        for prediction in predictions_to_print:
            print(prediction)

        self.output_label.set_text(output_text)

if __name__ == "__main__":
    app = ImagePredictorApp()
    app.connect("destroy", Gtk.main_quit)
    app.show_all()
    Gtk.main()