[gimp/wip/animation: 365/373] plug-ins: allow interrupting animation export.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/animation: 365/373] plug-ins: allow interrupting animation export.
- Date: Sat, 7 Oct 2017 02:25:24 +0000 (UTC)
commit c0d0266a1b8af3e5e8135d75344127cb27c16297
Author: Jehan <jehan girinstud io>
Date: Wed Aug 2 22:37:19 2017 +0200
plug-ins: allow interrupting animation export.
Adding a return value to the "loading" signal so that an export can be
interrupted in the middle (rather than waiting for the end of a long
export if you discover you made a mistake). When an export is in
progress, the export button becomes an export-cancel button.
Also add a parameterized progression message.
Finally make the full window non sensitive, but for the export-cancel
button.
plug-ins/animation-play/core/animation.c | 97 ++++++++-------
plug-ins/animation-play/core/animation.h | 11 +-
.../widgets/animation-dialog-export.c | 20 +++-
plug-ins/animation-play/widgets/animation-dialog.c | 126 ++++++++++++++------
4 files changed, 162 insertions(+), 92 deletions(-)
---
diff --git a/plug-ins/animation-play/core/animation.c b/plug-ins/animation-play/core/animation.c
index b387912..959efc0 100644
--- a/plug-ins/animation-play/core/animation.c
+++ b/plug-ins/animation-play/core/animation.c
@@ -46,10 +46,10 @@ enum
LOADING,
LOADED,
SIZE_CHANGED,
- FRAMES_CHANGED,
- INVALIDATE_CACHE,
DURATION_CHANGED,
FRAMERATE_CHANGED,
+ FRAMES_CHANGED,
+ INVALIDATE_CACHE,
LAST_SIGNAL
};
@@ -106,22 +106,27 @@ animation_class_init (AnimationClass *klass)
* Animation::loading:
* @animation: the animation loading.
* @ratio: fraction loaded [0-1].
+ * @label: the text to show as progression message.
*
- * The ::loading signal must be emitted by a subclass of Animation.
- * It can be used by a GUI to display a progress bar during loading.
+ * The ::loading signal must be emitted when a long process is taking
+ * place and you want the GUI to display a progress bar.
* GUI widgets depending on a consistent state of @animation should
* become unresponsive.
+ *
+ * Returns: TRUE is the loading should be stopped, FALSE otherwise.
+ * This allows long loading to be stopped.
*/
animation_signals[LOADING] =
g_signal_new ("loading",
G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_FIRST,
+ G_SIGNAL_ACTION,
G_STRUCT_OFFSET (AnimationClass, loading),
NULL, NULL,
- g_cclosure_marshal_VOID__DOUBLE,
- G_TYPE_NONE,
- 1,
- G_TYPE_DOUBLE);
+ NULL,
+ G_TYPE_BOOLEAN,
+ 2,
+ G_TYPE_DOUBLE,
+ G_TYPE_STRING);
/**
* Animation::loaded:
* @animation: the animation loading.
@@ -163,6 +168,42 @@ animation_class_init (AnimationClass *klass)
G_TYPE_INT,
G_TYPE_INT);
/**
+ * Animation::duration:
+ * @animation: the animation.
+ * @duration: the new duration of @animation in number of frames.
+ *
+ * The ::duration signal must be emitted when the duration of
+ * @animation changes.
+ */
+ animation_signals[DURATION_CHANGED] =
+ g_signal_new ("duration-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (AnimationClass, duration_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+ /**
+ * Animation::framerate-changed:
+ * @animation: the animation.
+ * @framerate: the new framerate of @animation in frames per second.
+ *
+ * The ::framerate-changed signal is emitted when the framerate of
+ * @animation changes.
+ */
+ animation_signals[FRAMERATE_CHANGED] =
+ g_signal_new ("framerate-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (AnimationClass, framerate_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__DOUBLE,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_DOUBLE);
+ /**
* Animation::frames-changed:
* @animation: the animation.
* @position: the first frame position whose contents changed.
@@ -188,7 +229,7 @@ animation_class_init (AnimationClass *klass)
* @position: the first frame position whose contents changed.
* @length: the number of changed frames from @position.
*
- * The ::invalidate-cache signal must be emitted when one or more
+ * The ::invalidate-cache signal is emitted when one or more
* successive frames have to be updated. This is similar to
* ::frames-changed except that it forces the cache update even if the
* contents apparently did not change. It will also invalidate cache
@@ -205,42 +246,6 @@ animation_class_init (AnimationClass *klass)
2,
G_TYPE_INT,
G_TYPE_INT);
- /**
- * Animation::duration:
- * @animation: the animation.
- * @duration: the new duration of @animation in number of frames.
- *
- * The ::duration signal must be emitted when the duration of
- * @animation changes.
- */
- animation_signals[DURATION_CHANGED] =
- g_signal_new ("duration-changed",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (AnimationClass, duration_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__INT,
- G_TYPE_NONE,
- 1,
- G_TYPE_INT);
- /**
- * Animation::framerate-changed:
- * @animation: the animation.
- * @framerate: the new framerate of @animation in frames per second.
- *
- * The ::framerate-changed signal is emitted when the framerate of
- * @animation changes.
- */
- animation_signals[FRAMERATE_CHANGED] =
- g_signal_new ("framerate-changed",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (AnimationClass, framerate_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__DOUBLE,
- G_TYPE_NONE,
- 1,
- G_TYPE_DOUBLE);
object_class->finalize = animation_finalize;
object_class->set_property = animation_set_property;
diff --git a/plug-ins/animation-play/core/animation.h b/plug-ins/animation-play/core/animation.h
index da10205..3fb7274 100644
--- a/plug-ins/animation-play/core/animation.h
+++ b/plug-ins/animation-play/core/animation.h
@@ -42,22 +42,23 @@ struct _AnimationClass
/* Signals */
void (*loading) (Animation *animation,
- gdouble ratio);
+ gdouble ratio,
+ const gchar *label);
void (*loaded) (Animation *animation);
void (*size_changed) (Animation *animation,
gint width,
gint height);
+ void (*duration_changed) (Animation *animation,
+ gint duration);
+ void (*framerate_changed) (Animation *animation,
+ gdouble fps);
void (*frames_changed) (Animation *animation,
gint position,
gint length);
void (*invalidate_cache) (Animation *animation,
gint position,
gint length);
- void (*duration_changed) (Animation *animation,
- gint duration);
- void (*framerate_changed) (Animation *animation,
- gdouble fps);
/* These virtual methods must be implemented by any subclass. */
gint (*get_duration) (Animation *animation);
diff --git a/plug-ins/animation-play/widgets/animation-dialog-export.c
b/plug-ins/animation-play/widgets/animation-dialog-export.c
index b8d2fbd..7bdcd82 100644
--- a/plug-ins/animation-play/widgets/animation-dialog-export.c
+++ b/plug-ins/animation-play/widgets/animation-dialog-export.c
@@ -137,6 +137,8 @@ animation_dialog_export_video (AnimationPlayback *playback,
GeglNode *graph;
GeglNode *export;
GeglNode *input;
+ gchar *label = g_strdup_printf(_("Exporting \"%s\""),
+ filename);
gint duration;
gint i;
@@ -158,14 +160,19 @@ animation_dialog_export_video (AnimationPlayback *playback,
for (i = 0; i < duration; i++)
{
GeglBuffer *buffer;
+ gboolean stops_loading;
g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) duration - 0.999));
+ (gdouble) i / ((gdouble) duration - 0.999),
+ label, &stops_loading);
+ if (stops_loading)
+ break;
buffer = animation_playback_get_buffer (playback, i);
gegl_node_set (input, "buffer", buffer, NULL);
gegl_node_process (export);
g_object_unref (buffer);
}
+ g_free (label);
g_object_unref (graph);
g_signal_emit_by_name (animation, "loaded");
}
@@ -202,9 +209,18 @@ animation_dialog_export_images (AnimationPlayback *playback,
{
GeglBuffer *buffer;
gchar *path;
+ gboolean stops_loading;
+
+ /* Make sure GUI interactions are processed, so that one can
+ * cancel the export anytime. */
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, FALSE);
g_signal_emit_by_name (animation, "loading",
- (gdouble) i / ((gdouble) duration - 0.999));
+ (gdouble) i / ((gdouble) duration - 0.999),
+ _("Exporting frames"), &stops_loading);
+ if (stops_loading)
+ break;
path = g_strdup_printf ("%s-%.*d.%s",
filename,
(gint) floor (log10(duration)) + 1,
diff --git a/plug-ins/animation-play/widgets/animation-dialog.c
b/plug-ins/animation-play/widgets/animation-dialog.c
index 51bd557..dd2d5c4 100755
--- a/plug-ins/animation-play/widgets/animation-dialog.c
+++ b/plug-ins/animation-play/widgets/animation-dialog.c
@@ -82,6 +82,7 @@ struct _AnimationDialogPrivate
GtkWidget *zoomcombo;
GtkWidget *refresh;
GtkWidget *export;
+ gboolean cancel_export;
GtkWidget *scrolled_drawing_area;
GtkWidget *drawing_area;
@@ -158,6 +159,8 @@ static gboolean on_dialog_expose (GtkWidget *widget,
static void update_ui_sensitivity (AnimationDialog *dialog);
/* UI callbacks */
+static void on_cancel_export (GtkToolButton *button,
+ AnimationDialog *dialog);
static void export_callback (GtkAction *action,
AnimationDialog *dialog);
static void close_callback (GtkAction *action,
@@ -226,8 +229,13 @@ static gboolean popup_menu (GtkWidget *widget,
AnimationDialog *dialog);
/* Animation Signals */
-static void show_loading_progress (Animation *animation,
+static gboolean show_loading_progress (Animation *animation,
+ gdouble load_rate,
+ const gchar *label,
+ AnimationDialog *dialog);
+static gboolean check_cancel_loading (Animation *animation,
gdouble load_rate,
+ const gchar *label,
AnimationDialog *dialog);
static void playback_range_changed (AnimationPlayback *playback,
gint playback_start,
@@ -290,11 +298,6 @@ static gboolean on_progress_event (GtkWidget *widget,
static void show_playing_progress (AnimationDialog *dialog);
/* Utils */
-static void inactive_on_loading (Animation *animation,
- gdouble load_rate,
- GtkWidget *widget);
-static void active_on_loaded (Animation *animation,
- GtkWidget *widget);
static void hide_on_animatic (AnimationDialog *dialog,
GParamSpec *param_spec,
GtkWidget *widget);
@@ -716,7 +719,7 @@ animation_dialog_constructed (GObject *object)
gtk_widget_show (priv->zoomcombo);
/* Export. */
- priv->export = GTK_WIDGET (gtk_tool_button_new (NULL, N_("Export the animation")));
+ priv->export = GTK_WIDGET (gtk_tool_button_new (NULL, NULL));
gtk_activatable_set_related_action (GTK_ACTIVATABLE (priv->export),
gtk_action_group_get_action (priv->various_actions, "export"));
@@ -1003,18 +1006,6 @@ animation_dialog_set_property (GObject *object,
case PROP_ANIMATION:
g_clear_object (&priv->animation);
priv->animation = g_value_get_object (value);
- g_signal_connect (priv->animation, "loading",
- (GCallback) inactive_on_loading,
- priv->settings);
- g_signal_connect (priv->animation, "loaded",
- (GCallback) active_on_loaded,
- priv->settings);
- g_signal_connect (priv->animation, "loading",
- (GCallback) inactive_on_loading,
- priv->upper_bar);
- g_signal_connect (priv->animation, "loaded",
- (GCallback) active_on_loaded,
- priv->upper_bar);
break;
default:
@@ -1158,7 +1149,7 @@ ui_manager_new (AnimationDialog *dialog)
static GtkActionEntry various_entries[] =
{
{ "export", GIMP_ICON_DOCUMENT_SAVE,
- NULL, "<control>e", N_("Export the video"),
+ NULL, "<control>e", N_("Export the animation"),
G_CALLBACK (export_callback) },
{ "help", "help-browser",
@@ -1513,6 +1504,9 @@ animation_dialog_set_animation (AnimationDialog *dialog,
g_signal_connect (priv->animation, "loading",
(GCallback) show_loading_progress,
dialog);
+ g_signal_connect (priv->animation, "loading",
+ (GCallback) check_cancel_loading,
+ dialog);
g_signal_connect_swapped (priv->animation, "loaded",
(GCallback) update_progress,
dialog);
@@ -1581,12 +1575,63 @@ update_ui_sensitivity (AnimationDialog *dialog)
/**** UI CALLBACKS ****/
static void
+on_cancel_export (GtkToolButton *button,
+ AnimationDialog *dialog)
+{
+ GET_PRIVATE (dialog)->cancel_export = TRUE;
+}
+
+static void
export_callback (GtkAction *action,
AnimationDialog *dialog)
{
AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
- animation_dialog_export (GTK_WINDOW (dialog), priv->playback);
+ if (! priv->cancel_export)
+ {
+ priv->cancel_export = FALSE;
+ /* Change the button icon and tooltip. */
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (priv->export),
+ "process-stop");
+ gtk_widget_set_tooltip_text (priv->export, _("Stop the Export"));
+
+ /* Make all but this button sensitive. */
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->play_bar), FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->right_pane), FALSE);
+ if (priv->xsheet)
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->xsheet), FALSE);
+ gtk_container_foreach (GTK_CONTAINER (priv->upper_bar),
+ (GtkCallback) gtk_widget_set_sensitive,
+ GINT_TO_POINTER (FALSE));
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->export), TRUE);
+
+ /* Run the export. */
+ g_signal_connect (priv->export, "clicked",
+ G_CALLBACK (on_cancel_export),
+ dialog);
+ animation_dialog_export (GTK_WINDOW (dialog), priv->playback);
+ g_signal_handlers_disconnect_by_func (priv->export,
+ G_CALLBACK (on_cancel_export),
+ dialog);
+
+ /* Reset icon and tooltip. */
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (priv->export),
+ GIMP_ICON_DOCUMENT_SAVE);
+ gtk_widget_set_tooltip_text (priv->export, _("Export the animation"));
+
+ /* Reset to normal sensitivity of the GUI. */
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->play_bar), TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->right_pane), TRUE);
+ if (priv->xsheet)
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->xsheet), TRUE);
+ gtk_container_foreach (GTK_CONTAINER (priv->upper_bar),
+ (GtkCallback) gtk_widget_set_sensitive,
+ GINT_TO_POINTER (TRUE));
+ }
+ else
+ {
+ priv->cancel_export = FALSE;
+ }
}
static void
@@ -2171,26 +2216,44 @@ popup_menu (GtkWidget *widget,
return TRUE;
}
-static void
+static gboolean
show_loading_progress (Animation *animation,
gdouble load_rate,
+ const gchar *label,
AnimationDialog *dialog)
{
AnimationDialogPrivate *priv = GET_PRIVATE (dialog);
- gchar *text;
+ const gchar *progress_label;
+ gchar *text;
+
+ if (label)
+ progress_label = label;
+ else
+ progress_label = _("Loading animation");
block_ui (dialog);
/* update the dialog's progress bar */
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progress), load_rate);
- text = g_strdup_printf (_("Loading animation %d %%"), (gint) (load_rate * 100));
+ text = g_strdup_printf ("%s %d %%", progress_label, (gint) (load_rate * 100));
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (priv->progress), text);
g_free (text);
/* Forcing the UI to update even with intensive computation. */
while (gtk_events_pending ())
gtk_main_iteration ();
+
+ return FALSE;
+}
+
+static gboolean
+check_cancel_loading (Animation *animation,
+ gdouble load_rate,
+ const gchar *label,
+ AnimationDialog *dialog)
+{
+ return GET_PRIVATE (dialog)->cancel_export;
}
static void
@@ -2968,21 +3031,6 @@ show_playing_progress (AnimationDialog *dialog)
}
static void
-inactive_on_loading (Animation *animation,
- gdouble load_rate,
- GtkWidget *widget)
-{
- gtk_widget_set_sensitive (widget, FALSE);
-}
-
-static void
-active_on_loaded (Animation *animation,
- GtkWidget *widget)
-{
- gtk_widget_set_sensitive (widget, TRUE);
-}
-
-static void
hide_on_animatic (AnimationDialog *dialog,
GParamSpec *param_spec,
GtkWidget *widget)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]