Permalink
Browse files

Merge remote-tracking branch 'origin/master' into review/dont-multipl…

…y-all-widgets
  • Loading branch information...
MaZderMind committed Jun 2, 2018
2 parents 8707a24 + 19e1922 commit 39e44680a27bb1f88b151e4979c61e26960917c9
View
@@ -13,6 +13,7 @@ def __init__(self, pipeline):
self.log = logging.getLogger('ControlServerCommands')
self.pipeline = pipeline
self.stored_values = {}
self.sources = Config.getlist('mix', 'sources')
if Config.getboolean('stream-blanker', 'enabled'):
@@ -26,6 +27,25 @@ def message(self, *args):
user-defined scripts. does not change the state of the voctocore."""
return NotifyResponse('message', *args)
def store_value(self, key, *args):
"""stores a value from a user-defined script in voctomix' memory.
setting a value triggers a 'value'-broadcast.
the value can be later retrieved using fetch_value.
does not change the state of the voctocore."""
value = ' '.join(args)
self.stored_values[key] = value
return NotifyResponse('value', key, value)
def fetch_value(self, key):
"""retrieves a previusly stored value from a user-defined script.
does not change the state of the voctocore."""
try:
value = self.stored_values[key]
except KeyError:
value = ""
return OkResponse('value', key, value)
def help(self):
"""displays help-messages for all commands"""
helplines = []
@@ -147,6 +167,12 @@ def get_composite_mode(self):
status = self._get_composite_status()
return OkResponse('composite_mode', status)
def get_composite_modes(self):
"""lists the names of all available composite-mode"""
names = [mode.name for mode in CompositeModes]
namestr = ','.join(names)
return OkResponse('composite_modes', namestr)
def get_composite_mode_and_video_status(self):
"""retrieves the composite-mode and the video-status
in a single call"""
@@ -235,6 +261,11 @@ def get_config(self):
for header, section in dict(Config).items()}
return OkResponse('server_config', json.dumps(confdict))
def get_config_option(self, section, key):
"""returns a single value from the server-config"""
value = Config.get(section, key)
return OkResponse('server_config_option', section, key, value)
def restart_source(self, src_name):
"""restarts the specified source"""
restart_source(src_name)
@@ -0,0 +1,13 @@
from lib.response import OkResponse
from tests.commands.commands_test_base import CommandsTestBase
class GetCompositeModesTest(CommandsTestBase):
def test_get_composite_modes(self):
response = self.commands.get_composite_modes()
self.assertIsInstance(response, OkResponse)
self.assertEqual(response.args, (
'composite_modes',
'fullscreen,side_by_side_equal,side_by_side_preview,picture_in_picture'
))
@@ -0,0 +1,23 @@
import configparser
from lib.config import Config
from lib.response import OkResponse
from tests.commands.commands_test_base import CommandsTestBase
class GetConfigOptionTest(CommandsTestBase):
def test_get_config_option(self):
Config.given('somesection', 'somekey', 'somevalue')
response = self.commands.get_config_option('somesection', 'somekey')
self.assertIsInstance(response, OkResponse)
self.assertEqual(response.args, ('server_config_option', 'somesection', 'somekey', 'somevalue'))
def test_get_option_from_unknown_config_section_fails(self):
with self.assertRaises(configparser.NoSectionError):
self.commands.get_config_option('othersection', 'otherkey')
def test_get_unknown_config_option_fails(self):
Config.given('somesection', 'somekey', 'somevalue')
with self.assertRaises(configparser.NoOptionError):
self.commands.get_config_option('somesection', 'otherkey')
@@ -0,0 +1,45 @@
from mock import ANY
from lib.response import NotifyResponse, OkResponse
from tests.commands.commands_test_base import CommandsTestBase
class SetStoreFetchValueTest(CommandsTestBase):
def test_store_value(self):
response = self.commands.store_value('somekey', 'some-value')
self.assertIsInstance(response, NotifyResponse)
self.assertEqual(response.args, ('value', 'somekey', 'some-value'))
def test_store_json_value(self):
response = self.commands.store_value(
'somekey',
'{"json": ["rigid", "better for data interchange"]}')
self.assertIsInstance(response, NotifyResponse)
self.assertEqual(response.args, (
'value',
'somekey',
'{"json": ["rigid", "better for data interchange"]}'
))
def test_retrieve_value(self):
self.commands.store_value('somekey', 'some-value')
response = self.commands.fetch_value('somekey')
self.assertIsInstance(response, OkResponse)
self.assertEqual(response.args, ('value', 'somekey', 'some-value'))
def test_latest_store_overwrites_value(self):
self.commands.store_value('somekey', 'some-value')
self.commands.store_value('somekey', 'another-value')
response = self.commands.fetch_value('somekey')
self.assertIsInstance(response, OkResponse)
self.assertEqual(response.args, ('value', 'somekey', 'another-value'))
def test_unknown_key_returns_empty_string(self):
response = self.commands.fetch_value('otherkey')
self.assertIsInstance(response, OkResponse)
self.assertEqual(response.args, ('value', 'otherkey', ''))
@@ -1,26 +1,22 @@
import logging
import math
import cairo
from gi.repository import Gtk, GLib
class AudioLevelDisplay(object):
"""Displays a Level-Meter of another VideoDisplay into a GtkWidget"""
def __init__(self, drawing_area):
self.log = logging.getLogger(
'AudioLevelDisplay[{}]'.format(drawing_area.get_name())
)
self.drawing_area = drawing_area
class AudioLevelDisplay(Gtk.DrawingArea):
"""Displays a Level-Meter of another VideoDisplay into a GtkWidget"""
__gtype_name__ = 'AudioLevelDisplay'
def __init__(self):
self.levelrms = []
self.levelpeak = []
self.leveldecay = []
self.height = -1
# register on_draw handler
self.drawing_area.connect('draw', self.on_draw)
self.connect('draw', self.draw_callback)
# generate gradient from green to yellow to red in logarithmic scale
def gradient(self, brightness, darkness, height):
@@ -35,15 +31,15 @@ def gradient(self, brightness, darkness, height):
# return result
return lg
def on_draw(self, widget, cr):
def draw_callback(self, widget, cr):
# number of audio-channels
channels = len(self.levelrms)
if channels == 0:
return False
width = self.drawing_area.get_allocated_width()
height = self.drawing_area.get_allocated_height()
width = self.get_allocated_width()
height = self.get_allocated_height()
# space between the channels in px
margin = 2
@@ -139,4 +135,4 @@ def level_callback(self, rms, peak, decay):
self.levelrms = rms
self.levelpeak = peak
self.leveldecay = decay
self.drawing_area.queue_draw()
self.queue_draw()
@@ -12,26 +12,27 @@ class StudioClock(Gtk.ToolItem):
# set resolution of the update timer in seconds
timer_resolution = 0.1
last_draw_time = time.localtime(0)
# init widget
def __init__(self):
super().__init__()
# suggest size of widget
self.set_size_request(130, 50)
# remember last drwn time
self.time = time.localtime(0)
# remember last draw time
self.last_draw_time = time.time()
# set up timeout for periodic redraw
GLib.timeout_add_seconds(self.timer_resolution, self.do_timeout)
def do_timeout(self):
# get current time
t = time.localtime(time.time())
current_time = time.time()
# if time did not change since last redraw
if self.time != t:
self.time = t
if current_time - self.last_draw_time >= 1.0:
self.last_draw_time = current_time
self.queue_draw()
# just come back
GLib.timeout_add_seconds(self.timer_resolution, self.do_timeout)
return True
# override drawing of the widget
def do_draw(self, cr):
@@ -51,10 +52,11 @@ def do_draw(self, cr):
cr.set_source(bg_lg)
cr.arc(center[0], center[1], radius, 0, 2 * math.pi)
cr.fill()
local_time = time.localtime(self.last_draw_time)
# draw ticks for every second
for tick in range(0, 60):
# fade out seconds in future and highlight past seconds
if tick > self.time.tm_sec:
if tick > local_time.tm_sec:
cr.set_source_rgb(0.2, 0.3, 0.01)
else:
cr.set_source_rgb(0.764, 0.804, 0.176)
View
@@ -53,10 +53,9 @@ def setup(self):
# Connect Close-Handler
self.win.connect('delete-event', Gtk.main_quit)
# Create Audio-Level Display
drawing_area = self.find_widget_recursive(self.win, 'audiolevel_main')
self.audio_level_display = AudioLevelDisplay(drawing_area)
# Get Audio-Level Display
self.audio_level_display = self.find_widget_recursive(
self.win, 'audiolevel_main')
# Create Main-Video Overlay Controller
drawing_area = self.find_widget_recursive(self.win,
'video_overlay_drawingarea')
@@ -100,8 +99,6 @@ def setup(self):
uibuilder=self
)
toolbar.insert(StudioClock(), len(toolbar.get_children()) - 1)
# Setup Shortcuts window
self.win.connect('key-press-event', self.handle_keypress)
self.win.connect('window-state-event', self.handle_state)
View
@@ -166,6 +166,16 @@
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="StudioClock" id="studioclock">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="close">
<property name="visible">True</property>
@@ -244,7 +254,7 @@
</packing>
</child>
<child>
<object class="GtkDrawingArea" id="audiolevel_main">
<object class="AudioLevelDisplay" id="audiolevel_main">
<property name="width_request">30</property>
<property name="visible">True</property>
<property name="can_focus">False</property>

0 comments on commit 39e4468

Please sign in to comment.