python-3.xmatplotlibtkinterrasterio

Impossible to change drag and dropped (with tkinterdnd2) raster when plotting


When running the following Python code, I'm able to change the raster (by drag and dropping a new raster file) and display its name in anew window as many times as I want (without closing the selection window and re-running the code). But as soon as I plot the raster distribution, I cannot change the raster file anymore. Which is what I want.

import os
import tkinter as tk
from tkinterdnd2 import TkinterDnD, DND_FILES
import rasterio
import matplotlib.pyplot as plt
from tkinter import messagebox

class SimpleApp(TkinterDnD.Tk):
    def __init__(self):
        super().__init__()
        self.title("Simple File Selector")
        self.geometry("400x400")

        self.file_label = tk.Label(self, text="Drop a file here", relief="solid", width=40, height=5)
        self.file_label.pack(pady=20)
        self.file_label.drop_target_register(DND_FILES)
        self.file_label.dnd_bind('<<Drop>>', self.load_file)

        self.print_button = tk.Button(self, text="Display Filename", command=self.display_filename)
        self.print_button.pack(pady=20)

        self.plot_button = tk.Button(self, text="Plot Distribution", command=self.plot_distribution)
        self.plot_button.pack(pady=20)

    def load_file(self, event):
        self.file_path = event.data.strip('{}')
        self.file_label.config(text=os.path.basename(self.file_path) if self.file_path else "No file selected")
        try:
            with rasterio.open(self.file_path) as src:
                self.raster_data = src.read(1)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to read raster file: {e}")
            self.file_label.config(text="Drop a file here")
            self.raster_data = None

    def display_filename(self):
        if hasattr(self, 'file_path') and self.file_path:
            filename_window = tk.Toplevel(self)
            filename_window.title("Selected File")
            filename_window.geometry("300x100")
            filename_label = tk.Label(filename_window, text=self.file_path, wraplength=280)
            filename_label.pack(pady=20)
        else:
            messagebox.showerror("Error", "No file selected")

    def plot_distribution(self):
        if hasattr(self, 'raster_data') and self.raster_data is not None:
            plt.figure()
            plt.hist(self.raster_data.flatten(), bins=50, color='blue', edgecolor='black')
            plt.title('Raster Data Distribution')
            plt.xlabel('Value')
            plt.ylabel('Frequency')
            plt.show()
        else:
            messagebox.showerror("Error", "No raster data to plot")

if __name__ == "__main__":
    app = SimpleApp()
    app.mainloop()

Solution

  • Inspiring from TheLizzard link, I was able to fix the issue. Thanks to him.

    Here is the fixed code:

    import os
    import tkinter as tk
    from tkinterdnd2 import TkinterDnD, DND_FILES
    import rasterio
    import matplotlib.pyplot as plt
    from tkinter import messagebox
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
    from matplotlib.figure import Figure
    
    class SimpleApp(TkinterDnD.Tk):
        def __init__(self):
            super().__init__()
            self.title("Simple File Selector")
            self.geometry("400x400")
    
            self.file_label = tk.Label(self, text="Drop a file here", relief="solid", width=40, height=5)
            self.file_label.pack(pady=20)
            self.file_label.drop_target_register(DND_FILES)
            self.file_label.dnd_bind('<<Drop>>', self.load_file)
    
            self.print_button = tk.Button(self, text="Display Filename", command=self.display_filename)
            self.print_button.pack(pady=20)
    
            self.plot_button = tk.Button(self, text="Plot Distribution", command=self.plot_distribution)
            self.plot_button.pack(pady=20)
    
            self.file_path = None
            self.raster_data = None
    
        def load_file(self, event):
            self.file_path = event.data.strip('{}')
            self.file_label.config(text=os.path.basename(self.file_path) if self.file_path else "No file selected")
            try:
                with rasterio.open(self.file_path) as src:
                    self.raster_data = src.read(1)
            except Exception as e:
                messagebox.showerror("Error", f"Failed to read raster file: {e}")
                self.file_label.config(text="Drop a file here")
                self.raster_data = None
    
        def display_filename(self):
            if self.file_path:
                filename_window = tk.Toplevel(self)
                filename_window.title("Selected File")
                filename_window.geometry("300x100")
                filename_label = tk.Label(filename_window, text=self.file_path, wraplength=280)
                filename_label.pack(pady=20)
            else:
                messagebox.showerror("Error", "No file selected")
    
        def plot_distribution(self):
            if self.raster_data is not None:
                plot_window = tk.Toplevel(self)
                plot_window.title("Raster Data Distribution")
                plot_window.geometry("800x600")
    
                fig = Figure(figsize=(6, 4), dpi=100)
                ax = fig.add_subplot(111)
                ax.hist(self.raster_data.flatten(), bins=50, color='blue', edgecolor='black')
                ax.set_title('Raster Data Distribution')
                ax.set_xlabel('Value')
                ax.set_ylabel('Frequency')
    
                canvas = FigureCanvasTkAgg(fig, master=plot_window)
                canvas.draw()
                canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
    
                toolbar_frame = tk.Frame(plot_window)
                toolbar_frame.pack(side=tk.BOTTOM, fill=tk.X)
                toolbar = NavigationToolbar2Tk(canvas, toolbar_frame)
                toolbar.update()
                canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
            else:
                messagebox.showerror("Error", "No raster data to plot")
    
    if __name__ == "__main__":
        app = SimpleApp()
        app.mainloop()