Public Access
add toolbar buttons copying the plot as PNG and SVG to the clipboard
Two new buttons below the plot copy the current figure to the clipboard. The PNG copy renders off-screen on a temporary Agg canvas at a fixed 12:8 inches and 800 px width, independent of the window size, and restores figure canvas, size and dpi afterwards so the on-screen plot is untouched. The RGBA buffer is wrapped in GLib.Bytes for GdkPixbuf.Pixbuf.new_from_bytes, which current PyGObject requires (fixes the 'Expected GLib.Bytes, but got bytes' error). The SVG copy keeps using savefig into a text buffer; both report through logger. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
1 parent
0135f9b7a3
commit
1bed381639
2 files changed
+80
-33
No files matched your search
@@ -253,6 +253,8 @@ class DamarisGUI:
|
||||
"on_display_y_scaling_combobox_changed": self.monitor.display_scaling_changed,
|
||||
"on_display_save_data_as_text_button_clicked": self.monitor.save_display_data_as_text,
|
||||
"on_display_copy_data_to_clipboard_button_clicked": self.monitor.copy_display_data_to_clipboard,
|
||||
"on_display_copy_png_button_clicked": self.monitor.copy_canvas_as_png,
|
||||
"on_display_copy_svg_button_clicked": self.monitor.copy_canvas_as_svg,
|
||||
"on_experiment_script_textview_event": self.sw.textviews_modified,
|
||||
"on_main_notebook_switch_page": self.nopevent,
|
||||
"main_window_close": self.nopevent
|
||||
@@ -3832,25 +3834,20 @@ class MonitorWidgets:
|
||||
return
|
||||
|
||||
try:
|
||||
# Save the original figure size
|
||||
original_size = self.matplot_figure.get_size_inches()
|
||||
# Save figure to a buffer as SVG
|
||||
import io as iomodule
|
||||
buf = iomodule.StringIO()
|
||||
self.matplot_figure.set_size_inches(6, 4)
|
||||
self.matplot_figure.savefig(buf, format='svg', bbox_inches='tight')
|
||||
buf.seek(0)
|
||||
|
||||
# Copy SVG text to clipboard
|
||||
clipboard = gtk.Clipboard.get(gdk.SELECTION_CLIPBOARD)
|
||||
clipboard.set_text(buf.read(), -1)
|
||||
clipboard.store()
|
||||
self.matplot_figure.set_size_inches(original_size)
|
||||
|
||||
log("Figure copied to clipboard as SVG")
|
||||
logger.info("Figure copied to clipboard as SVG")
|
||||
|
||||
except Exception as e:
|
||||
log(f"Error copying figure as SVG: {e}")
|
||||
logger.error("Error copying figure as SVG: %s", e)
|
||||
|
||||
def copy_display_data_to_clipboard( self, widget, data=None ):
|
||||
"""
|
||||
@@ -3858,38 +3855,42 @@ class MonitorWidgets:
|
||||
Also keep text data copy functionality as fallback
|
||||
"""
|
||||
# First, try to copy the matplotlib figure as image
|
||||
# Save the original figure size
|
||||
original_size = self.matplot_figure.get_size_inches()
|
||||
if self.matplot_figure is not None and self.matplot_canvas is not None:
|
||||
try:
|
||||
# Save figure to a temporary buffer as PNG
|
||||
import io as iomodule
|
||||
buf = iomodule.BytesIO()
|
||||
self.matplot_figure.set_size_inches(6, 4)
|
||||
self.matplot_figure.savefig(buf, format='png', dpi=300, bbox_inches='tight')
|
||||
buf.seek(0)
|
||||
|
||||
# Load the PNG data into a GdkPixbuf
|
||||
from gi.repository import GdkPixbuf
|
||||
loader = GdkPixbuf.PixbufLoader.new_with_type('png')
|
||||
loader.write(buf.read())
|
||||
loader.close()
|
||||
pixbuf = loader.get_pixbuf()
|
||||
# Render off-screen at a fixed size (12:8 inches, 800 px
|
||||
# wide) on a temporary Agg canvas. Snapshotting the GTK
|
||||
# canvas buffer instead would copy the window size, and
|
||||
# re-rendering through the GTK canvas would replace its
|
||||
# renderer and leave the on-screen plot blank.
|
||||
from matplotlib.backends.backend_agg import FigureCanvasAgg
|
||||
figure = self.matplot_figure
|
||||
orig_canvas = figure.canvas
|
||||
orig_size = figure.get_size_inches( )
|
||||
orig_dpi = figure.dpi
|
||||
try:
|
||||
agg_canvas = FigureCanvasAgg( figure )
|
||||
figure.set_size_inches( 12, 8 )
|
||||
figure.dpi = 800 / 12
|
||||
agg_canvas.draw( )
|
||||
rgba = numpy.asarray( agg_canvas.buffer_rgba( ) )
|
||||
finally:
|
||||
figure.set_canvas( orig_canvas )
|
||||
figure.set_size_inches( *orig_size )
|
||||
figure.dpi = orig_dpi
|
||||
height, width = rgba.shape[ :2 ]
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_bytes(
|
||||
glib.Bytes.new( rgba.tobytes( ) ), GdkPixbuf.Colorspace.RGB, True, 8,
|
||||
width, height, width * 4 )
|
||||
|
||||
# Copy to clipboard
|
||||
clipboard = gtk.Clipboard.get(gdk.SELECTION_CLIPBOARD)
|
||||
clipboard.set_image(pixbuf)
|
||||
clipboard.store()
|
||||
# Restore original figure size
|
||||
self.matplot_figure.set_size_inches(original_size)
|
||||
# Redraw the canvas to ensure it stays visible
|
||||
self.matplot_canvas.draw()
|
||||
clipboard = gtk.Clipboard.get( gdk.SELECTION_CLIPBOARD )
|
||||
clipboard.set_image( pixbuf )
|
||||
|
||||
log("Figure copied to clipboard as PNG image")
|
||||
logger.info("Figure copied to clipboard as PNG image")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
log("Error copying figure to clipboard: %s" % str(e))
|
||||
logger.error("Error copying figure to clipboard: %s", e)
|
||||
|
||||
##################### functions to feed display
|
||||
def clear_display( self ):
|
||||
|
||||
@@ -1414,10 +1414,56 @@ Public License instead of this License.
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<object class="GtkButton" id="display_copy_png_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Copy plot as PNG image to clipboard</property>
|
||||
<signal name="clicked" handler="on_display_copy_png_button_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_copy_png">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="xpad">10</property>
|
||||
<property name="label" translatable="yes"><span foreground="#0000ff">PNG</span></property>
|
||||
<property name="use-markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">3</property>
|
||||
<property name="right-attach">4</property>
|
||||
<property name="top-attach">1</property>
|
||||
<property name="bottom-attach">2</property>
|
||||
<property name="x-options"/>
|
||||
<property name="y-options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<object class="GtkButton" id="display_copy_svg_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Copy plot as SVG to clipboard</property>
|
||||
<signal name="clicked" handler="on_display_copy_svg_button_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_copy_svg">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="xpad">10</property>
|
||||
<property name="label" translatable="yes"><span foreground="#0000ff">SVG</span></property>
|
||||
<property name="use-markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">2</property>
|
||||
<property name="right-attach">3</property>
|
||||
<property name="top-attach">1</property>
|
||||
<property name="bottom-attach">2</property>
|
||||
<property name="x-options"/>
|
||||
<property name="y-options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
|
||||
Reference in new issue
Block a user