Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move viewer to JavisViewer #461

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
2 changes: 0 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ VideoIO = "d6d074c3-1acf-5d4c-9a43-ef38773959a2"
Animations = "0.4"
Cairo = "1"
FFMPEG = "0.3, 0.4"
Gtk = "1.1"
GtkReactive = "1.0.3"
Hungarian = "0.6"
ImageIO = "0.4, 0.5, 0.6"
ImageMagick = "1.1"
Expand Down
7 changes: 4 additions & 3 deletions src/Javis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ include("javis_viewer.jl")
include("latex.jl")
include("object_values.jl")


export setup_stream, cancel_stream

"""
projection(p::Point, l::Line)

Expand Down Expand Up @@ -267,8 +270,7 @@ function render(
return video, length(frames), objects

else
_javis_viewer(video, length(frames), objects)
return "Live Preview Started"
return video, length(frames), objects
end
end

Expand Down Expand Up @@ -739,7 +741,6 @@ export JBox, JCircle, JEllipse, JLine, JPoly, JRect, JStar, @JShape

# custom override of luxor extensions
export setline, setopacity, fontsize, get_fontsize, scale, text
export setup_stream, cancel_stream

# scales
export scale_linear, @scale_layer
Expand Down
237 changes: 0 additions & 237 deletions src/javis_viewer.jl
Original file line number Diff line number Diff line change
@@ -1,242 +1,5 @@
include("structs/Livestream.jl")

"""
_draw_image(video::Video, objects::Vector, frame::Int, canvas::Gtk.Canvas,
img_dims::Vector)

Internal function to create an image that is drawn on a Gtk Canvas.
"""
function _draw_image(
video::Video,
objects::Vector,
frame::Int,
canvas::Gtk.Canvas,
img_dims::Vector,
)
@guarded draw(canvas) do widget
# Gets a specific frame from graphic; transposed due to returned matrix
frame_mat = transpose(get_javis_frame(video, objects, frame; layers = video.layers))

# Gets the correct Canvas context to draw on
context = getgc(canvas)

# Uses Cairo to draw on Gtk canvas context
image(context, CairoImageSurface(frame_mat), 0, 0, img_dims[1], img_dims[2])
end
end

"""
_increment(video::Video, widgets::Vector, objects::Vector, dims::Vector,
canvas::Gtk.Canvas, frames::Int, layers=Vector)

Increments a given value and returns the associated frame.
"""
function _increment(
video::Video,
widgets::Vector,
objects::Vector,
dims::Vector,
canvas::Gtk.Canvas,
frames::Int,
)
# Get current frame from textbox as an Int value
curr_frame = parse(Int, get_gtk_property(widgets[2], :text, String))
if frames > curr_frame
# `widgets[1]` represents the GtkReactive slider widget
push!(widgets[1], curr_frame + 1)
_draw_image(video, objects, curr_frame + 1, canvas, dims)
else
# `widgets[2]` represents the GtkReactive textboxwidget
push!(widgets[2], 1) # Sets the first frame shown to one
_draw_image(video, objects, 1, canvas, dims)
end
end

"""
_decrement(video::Video, widgets::Vector, objects::Vector, dims::Vector,
canvas::Gtk.Canvas, frames::Int, layers::Vector)

Decrements a given value and returns the associated frame.
"""
function _decrement(
video::Video,
widgets::Vector,
objects::Vector,
dims::Vector,
canvas::Gtk.Canvas,
frames::Int,
)
# Get current frame from textbox as an Int value
curr_frame = parse(Int, get_gtk_property(widgets[2], :text, String))
if curr_frame > 1
# `widgets[1]` represents the GtkReactive slider widget
push!(widgets[1], curr_frame - 1)
_draw_image(video, objects, curr_frame - 1, canvas, dims)
else
# `widgets[2]` represents the GtkReactive textboxwidget
push!(widgets[2], frames) # Sets the first frame shown to one
_draw_image(video, objects, frames, canvas, dims)
end
end

"""
_javis_viewer(video::Video, frames::Int, object_list::Vector, show::Bool)

Internal Javis Viewer built on Gtk that is called for live previewing.
"""
function _javis_viewer(
video::Video,
total_frames::Int,
object_list::Vector,
show::Bool = true,
)
#####################################################################
# VIEWER WINDOW AND CONFIGURATION
#####################################################################

# Determine frame size of animation
frame_dims = [video.width, video.height]

# Creates a GTK window for drawing; sized based on frame size
win = GtkWindow("Javis Viewer", frame_dims[1], frame_dims[2])

# Sets border size of window
set_gtk_property!(win, :border_width, 20)

#####################################################################
# DISPLAY WIDGETS
#####################################################################

# Create GtkScale internal widget
_slide = GtkScale(false, 1:total_frames)

# Create GtkReactive slider widget
slide = slider(1:total_frames, value = 1, widget = _slide)

#=
#
# NOTE: We must provide a named GtkScale widget named `_slide` to the
# GtkReactive `slider` widget so as to perform asynchronous calls
# via signal_connect. Otherwise, we will be unable to update the
# widget that is automatically created by the slider object.
#
# It should be stated that a `slider` object is essentially a
# GtkScale widget coupled with a Reactive object.
#
=#

# Create a textbox
tbox = GtkReactive.textbox(Int; signal = signal(slide))

# Button for going forward through animation
forward = GtkButton("==>")

# Button for going backward through animation
backward = GtkButton("<==")

#=
TODO: Enable widgets of window to dynamically resize based on user changing the size of a window.
I think I can use the `configure-event` signal in GTK3 documentation
(link: https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-configure-event).
From there, I can then make a `signal_connect` set-up where I update `set_gtk_property!()`
of the windows accordingly using `:width_request` and `height_request`.
=#

#####################################################################
# VIEWER CANVAS AND GRID CONFIGURATION
#####################################################################

# Gtk Canvas object upon which to draw image; sized via frame size
canvas = Gtk.Canvas(frame_dims[1], frame_dims[2])

# Grid to allocate widgets
grid = Gtk.Grid()

# Allocate the widgets in a 3x3 grid
grid[1:3, 1] = canvas
grid[1:3, 2] = slide
grid[1, 3] = backward
grid[2, 3] = tbox
grid[3, 3] = forward

# Center all widgets vertically in grid
set_gtk_property!(grid, :valign, 3)

# Center all widgets horizontally in grid
set_gtk_property!(grid, :halign, 3)

# Adds grid to previously defined window
push!(win, grid)

#####################################################################
# DISPLAY FIRST FRAME
#####################################################################

_draw_image(video, object_list, 1, canvas, frame_dims)

#####################################################################
# SIGNAL CONNECTION FUNCTIONS
#####################################################################

# When the slider is changed, update currently viewed frame
signal_connect(_slide, "value-changed") do widget
# Collects GtkScale as an adjustable bounded value object
bound_slide = Gtk.GAccessor.adjustment(_slide)

# Get frame number from bounded value object as Int
slide_val = Gtk.get_gtk_property(bound_slide, "value", Int)

_draw_image(video, object_list, slide_val, canvas, frame_dims)
end

# When the `Enter` key is pressed, update the frame
signal_connect(win, "key-press-event") do widget, event
if event.keyval == 65293
# Get current frame from textbox as an Int value
curr_frame = parse(Int, get_gtk_property(tbox, :text, String))
curr_frame = clamp(curr_frame, 1, total_frames)
_draw_image(video, object_list, curr_frame, canvas, frame_dims)
end
end

# When the `forward` button is clicked, increment current frame number
# If at final frame, wrap viewer to first frame
signal_connect(forward, "clicked") do widget
_increment(video, [slide, tbox], object_list, frame_dims, canvas, total_frames)
end

# When the `Right Arrow` key is pressed, increment current frame number
# If at final frame, wrap viewer to first frame
signal_connect(win, "key-press-event") do widget, event
if event.keyval == 65363
_increment(video, [slide, tbox], object_list, frame_dims, canvas, total_frames)
end
end

# When the `backward` button is clicked, decrement the current frame number
# If at first frame, wrap viewer to last frame
signal_connect(backward, "clicked") do widget
_decrement(video, [slide, tbox], object_list, frame_dims, canvas, total_frames)
end

# When the `Left Arrow` key is pressed, decrement current frame number
# If at first frame, wrap viewer to last frame
signal_connect(win, "key-press-event") do widget, event
if event.keyval == 65361
_decrement(video, [slide, tbox], object_list, frame_dims, canvas, total_frames)
end
end

#####################################################################

if show
# Display image viewer
Gtk.showall(win)
else
return win, frame_dims, slide, tbox, canvas, object_list, total_frames, video
end
end

"""
setup_stream(livestreamto=:local; protocol="udp", address="0.0.0.0", port=14015, twitch_key="")

Expand Down
55 changes: 55 additions & 0 deletions test/livestream.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
function ground(args...)
background("white")
sethue("black")
end

@testset "Livestreaming" begin
astar(args...; do_action = :stroke) = star(O, 50, 5, 0.5, 0, do_action)
acirc(args...; do_action = :stroke) = circle(Point(100, 100), 50, do_action)

vid = Video(500, 500)
back = Background(1:100, ground)
star_obj = Object(1:100, astar)
act!(star_obj, Action(morph_to(acirc; do_action = :fill)))

conf_local = setup_stream(:local, address = "0.0.0.0", port = 8081)
@test conf_local isa Javis.StreamConfig
@test conf_local.livestreamto == :local
@test conf_local.protocol == "udp"
@test conf_local.address == "0.0.0.0"
@test conf_local.port == 8081

conf_twitch_err = setup_stream(:twitch)
conf_twitch = setup_stream(:twitch, twitch_key = "foo")
@test conf_twitch_err isa Javis.StreamConfig
@test conf_twitch_err.livestreamto == :twitch
@test isempty(conf_twitch_err.twitch_key)
@test conf_twitch.twitch_key == "foo"

render(vid, pathname = "stream_local.gif", streamconfig = conf_local)

# errors with macos; a good test to have
# test_local = run(pipeline(`lsof -i -P -n`, `grep ffmpeg`))
# @test test_local isa Base.ProcessChain
# @test test_local.processes isa Vector{Base.Process}

cancel_stream()
@test_throws ProcessFailedException run(
pipeline(
`ps aux`,
pipeline(`grep ffmpeg`, pipeline(`grep stream_loop`, `awk '{print $2}'`)),
),
)

vid = Video(500, 500)
back = Background(1:100, ground)
star_obj = Object(1:100, astar)
act!(star_obj, Action(morph_to(acirc; do_action = :fill)))

@test_throws ErrorException render(
vid,
pathname = "stream_twitch.gif",
streamconfig = conf_twitch_err,
)
rm("stream_twitch.gif")
end
4 changes: 2 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ end
@testset "Postprocessing" begin
include("postprocessing.jl")
end
@testset "Javis Viewer" begin
include("viewer.jl")
@testset "Javis LiveStream" begin
include("livestream.jl")
end
end
Loading