"""
Module: csengine/pluginbase.py
This module contains the Basic Plugin Class that needs to be inherited by all Plugins.
"""
import warnings
from kivy.uix.gridlayout import GridLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.modalview import ModalView
from kivy.uix.image import Image
from kivy.graphics import Color, Rectangle
from kivy.clock import Clock
from kivy.animation import Animation
from kivy.lang.builder import Builder
from kivy.core.window import Window
from csengine.utility import DebugUtility, PluginUtility, OSUtility, NotificationUtility
from csengine.globals import ActivePlugin
import csengine.globals as csglobal
from csengine.uix import PopUpYESNO, ItemInformationBox, ShowWaiting, RoundedButton
from csengine.utility import PluginUtility
from . import globals
from pathlib import Path
import random, math, threading
from functools import partial
from threading import Event
[docs]
class PluginBase:
#############################################################################################
# CORE FUNCTIONALITY METHODS. DO NOT MODIFY OR OVERRIDE
# If you want to know which functions you can override go to the section at the bottom
# of the code. modifyController(), play(), stop(), preload(), getFrame()
#############################################################################################
""" This is the base class for all plugins that need to be made for CamSkool application. All plugins need to inherit this class and overwrite the following functions:
* modifyController() - Override this function in subclass to modify the controller UI. If not overridden it will show an emtpy controller. Use self.controllerCanvas.add_widget(YOUR MAIN WIDGET HERE)
* getFrame() - This function is called every Frame. Override this function to change the frame t be shown
* dropFrame() - This function is called in case another frame is still being processes - so we drop one frame to hold the framerate
* play() - This function is called immediately after the Controller is created and made visible. Add any code in the function override that needs to run when the controller is visible.
* stop() - This function is called when the controller is closed and the confirm dialog is also YES
* preload() - Override this function to put anything that needs to be completed before the plugin opens.
:ivar normalBG: The path to the *Background Image* to be shown as a thumbnail in CamSkool for the plugin. (Default: "")
:vartype normalBG: str
:ivar downBG: The path to the *MouseOver Image* to be shown when the user mouseovers the thumbnail. (Default: "")
:vartype downBG: str
:ivar controllerBG: The path to the *Background Image* to be shown in the plugin's controller. (Default: "")
:vartype controllerBG: str
:ivar name: The name of the Plygin as shown in the application. (Default: "Sample Plugin")
:vartype name: str
:ivar description: The short description of the plugin that will be used in the app. (Default: "No Information available.")
:vartype description: str
:ivar tags_raw: The tags for the plugin that will be used in search. (Default: "plugin,camskool,untagged")
:vartype tags_raw: str
:ivar plugin_path: The path where the plugin will be installed or found. This path should always be CEEnvironment.GetFullPluginsPath() / plugin_name, unless you specifically want to install the plugin at a different location.
:vartype plugin_path: str
:ivar type: This value defines where the plugin will show up in the app. The type of plugin decides wehre it shows. The values have to be "ANIM", "IMAGE" or "TOOL". The names can be confusing because the design process and ideas about plugin types and categories changed over time but the values are still the same as the first categorisations. Changing this in the code is easy, but it will mean that all the plugins have to be recompiled and resigned and re-uploaded. (Default: "TOOL")
:vartype type: str
:ivar platform: This value was used to make sure that the platform specific plugins could be made. Check globals class for options like globals.COMPATIBILITY_ALL, globals.COMPATIBILITY_WIN_ONLY, globals.COMPATIBILITY_MAC_ONLY
:vartype platform: int
:ivar stackable: This value allows the application to know if this plugin can be stacked over other plugins or not. Some plugins cannot be stacked over other plugins because of their functionality, but most plugins can be. (Default: False)
:vartype stackable: bool
:ivar infoImagePath: This is the relative path for the Plugin Info Image.
:vartype infoImagePath: str
:ivar iconImage: This is the relative path for the Plugin's icon image.
:vartype iconImage: str
:ivar downloadLink: This is the url for this plugin's download link in the store.
:vartype downloadLink: str
"""
pipes = []
writerEvent = None
audioOverlayChannel = None
audioReRoute = False
requiresRGB = False
# The constructor __init__
def __init__(self, **kwargs):
self.normalBG = kwargs.get("normalBG", "")
self.downBG = kwargs.get("downBG", "")
self.controllerBG = kwargs.get("controllerBG", "")
self.name = kwargs.get("name", "Sample Plugin")
self.description = kwargs.get("description", "No Information available.")
self.tags_raw = kwargs.get("tags", "plugin,camskool,untagged")
self.plugin_path = kwargs.get("plugin_path", "")
self.plugin_name = kwargs.get("plugin_name", None)
self.type = kwargs.get("type", "TOOL")
self.platform = kwargs.get("platform", 0)
self.stackable = kwargs.get("stackable", False)
self.infoImagePath = kwargs.get("info_image", "./CommonMedia/sampleInfo.png")
self.iconImage = kwargs.get("icon", "./CommonMedia/sampleIcon.png")
if self.plugin_path is None:
return
self.originalInstance = self # will be used to connect multiple instances to original one.
self.tags = self.tags_raw.split(",")
self.controllerWidth = "360sp"
self.controllerHeight = '360sp'
self.iconSize = ("92sp", "92sp")
self.appWidth = 360
self.appHeight = 650
self.AddPluginToList()
self.controllerCanvas = None
self.controller = None
self.confirmOnClose = True
self.confirmCloseText = "Are you sure you want to remove [b]" + self.name + "[/b] " \
"from the stack? You will lose your settings."
self.addFlag = False
self.mainGridLayout = None
self.openButton = None
self.infoButton = None
self.favoriteButton = ToggleButton()
self.favoriteButtonCon = ToggleButton()
self.loader = None
self.preloadThread = None
self.preloadClock = None
self.preloadProgress = 0 # This value can be modified to change the progressbar status
self.loaderText = self.name + " is loading. Please wait."
self.ObservePlatformCompatibility()
PluginUtility.LoadFavoritesFromFile(self)
self.mainGL = None
self.controllerGL = None
#Threading Event to block writing function
self.writerEvent = Event()
self.writerEvent.set()
self.audioReRoute = False
## The Plugins UI on the List. This will remain as is. DO NO OVERRIDE
[docs]
def createPluginUI(self):
fav_state = "normal"
if self in PluginUtility.Plg_Favorites:
fav_state = "down"
mainGL = GridLayout(cols=1, size_hint=(None, None), width="170sp", height="130sp")
gL1 = GridLayout(cols=1, size_hint=(None, None), width="170sp", height="100sp")
gL2 = GridLayout(cols=5)
gL3 = GridLayout(cols=1, size_hint=(None, None), width="35sp", height="35sp")
gL4 = GridLayout(cols=1)
gL5 = GridLayout(cols=1, size_hint=(None, None), width="35sp", height="35sp")
gL6 = GridLayout(cols=3)
gL7 = GridLayout(cols=3, size_hint_y=None, height="50sp")
gL8 = GridLayout(cols=3, size_hint_y=None, height="60sp")
gL9 = GridLayout(cols=1)
self.mainGridLayout = gL1
tgl_fav = ToggleButton(text="", background_normal="./CommonMedia/fav_button.png",
background_down="./CommonMedia/fav_button_D.png", state=fav_state)
tgl_fav.bind(on_press=self.addRemoveFavorites)
btn_info = Button(text="", background_normal="./CommonMedia/info_button.png",
background_down="./CommonMedia/info_button_D.png")
btn_info.bind(on_release=self.showPluginInfo)
btn_open = Button(size_hint=(None, None), width="85sp", height="28sp", text="Open",color=(0,0,0,1),
background_normal="./CommonMedia/open_button.png",
background_down="./CommonMedia/open_button_D.png")
btn_open.bind(on_release=self.openPlugin)
self.openButton = btn_open
self.openButton.opacity = 0
self.infoButton = btn_info
self.favoriteButton = tgl_fav
gL1.add_widget(Label(size_hint_y=None, height="10sp"))
gL1.add_widget(gL2)
gL2.add_widget(Label(size_hint=(None, None), width="15sp", height="15sp"))
gL2.add_widget(gL3)
gL3.add_widget(btn_info)
gL2.add_widget(gL4)
gL4.add_widget(Label())
gL2.add_widget(gL5)
gL5.add_widget(tgl_fav)
gL2.add_widget(Label(text="", size_hint=(None, None), width="15sp", height="15sp"))
gL1.add_widget(gL6)
gL6.add_widget(Label())
gL6.add_widget(gL7)
gL7.add_widget(Label())
gL7.add_widget(Label())
gL7.add_widget(Label())
gL6.add_widget(Label())
gL1.add_widget(gL8)
gL8.add_widget(Label())
gL8.add_widget(btn_open)
gL8.add_widget(Label())
gL1.add_widget(gL9)
gL9.add_widget(Label(text="", size_hint_y=None, height="5sp"))
mainGL.add_widget(gL1)
mainGL.add_widget(Label(text=self.name, size_hint_y=None, height="13sp", color=(.5,.5,.5,1)))
gL1.bind(pos=self.onMouseOver) # Required as the GridLayout position values don't change.
# UPDATE MOVED TO MOUSE MOVE
Window.bind(mouse_pos=self.onMouseOver)
return mainGL
# Callback Function that updates the new positions of items inside Main GridLayout
# UPDATE > Not required anymore as the event is now triggered from Mouse Move
[docs]
def callback_posChange(self, instance, value):
"""
This function is deprecated and will be removed in a future version since it is no longer required.
"""
with instance.canvas.before:
Color(1, 1, 1, 1)
Rectangle(source=str(self.normalBG), pos=(instance.x, instance.y), size=(instance.width, instance.height))
warnings.warn(
"callback_posChange is deprecated and will be removed in a future version.",
DeprecationWarning,
stacklevel=2, # Important for correct line number in warning
)
# Call back function for Mouse Over.
[docs]
def onMouseOver(self, *args):
""" This function is called whenever there is a Mouseover detected on the thumbnail of the plugin on the Plugin List Screens of CamSkool.
Whatever changed need to be made to the plugin Thumbnail on mouseover need to be added here.
"""
if self.mainGridLayout is not None:
x = args[1][0]
y = args[1][1]
coord = self.mainGridLayout.to_widget(x=x, y=y,
relative=False) # IMPORTANT! TO MAP WINDOW X, Y COORD
# TO WIDGET COORDINATES
if self.mainGridLayout.collide_point(*coord):
self.mainGridLayout.canvas.before.clear() # Clear Canvas.Before
with self.mainGridLayout.canvas.before: # Add Updated Image
Color(1, 1, 1, 1)
Rectangle(source=str(self.downBG), pos=( self.mainGridLayout.x, self.mainGridLayout.y),
size=( self.mainGridLayout.width, self.mainGridLayout.height))
self.infoButton.opacity = 1
self.openButton.opacity = 1
self.favoriteButton.opacity = 1
else:
self.mainGridLayout.canvas.before.clear() # Clear Canvas.Before
with self.mainGridLayout.canvas.before: # Add Updated Image
Color(1, 1, 1, 1)
Rectangle(source=str(self.normalBG), pos=( self.mainGridLayout.x, self.mainGridLayout.y),
size=( self.mainGridLayout.width, self.mainGridLayout.height))
self.infoButton.opacity = 0
self.openButton.opacity = 0
if not self.favoriteButton.state == "down":
self.favoriteButton.opacity = 0
# This function creates the UI for the plugin in the search window
[docs]
def createSearchListUI(self):
""" The function that creates the UI for the plugin in the Search Window. This is different from the main screen list of plugins.
You can add/delete/modify anything on the plugin Search UI here.
"""
gL = GridLayout(cols=4, padding=["10sp", "0sp", "10sp", "0sp"], size_hint_y=None, height="50sp")
gLlbl = GridLayout(cols=1, size_hint_y="50sp")
gLBtn = GridLayout(cols=2, padding=["0sp", "10sp", "0sp", "10sp"], size_hint_y=None, height="50sp")
img = Image(source=str(self.iconImage), size_hint=(None, None), width="50sp", height="50sp")
btnInfo = RoundedButton(text="Info", on_release=self.showPluginInfo, background_color = (1, .506, 0.07, 1))
btnInfo.makeQuarterButton()
btnOpen = RoundedButton(text="Open", on_release=self.openPlugin, background_color=(0, 0.447, 0.737, 1))
btnOpen.makeQuarterButton()
# lblName = Label(text="[b]" + str(self.name) + "[/b]", markup=True , size_hint_x=None, width="162sp", halign="left", valign="middle")
lblNameKV = """
Label:
text: "{item}"
text_size: self.size
markup: True
size_hint_x: None
width: dp(165)
halign: "left"
valign: "middle"
"""
lblName = Builder.load_string(lblNameKV.format(item="[b] " + str(self.name) + "[/b]"))
gLBtn.add_widget(btnInfo)
gLBtn.add_widget(btnOpen)
gL.add_widget(img)
gL.add_widget(lblName)
gL.add_widget(gLBtn)
gL.bind(size=self.updateLayoutPosition)
return gL
# This function is the callback function for on Position or Size change of the Layout in SearchList UI
[docs]
def updateLayoutPosition(self, *args):
""" Call back function for UI updates"""
with args[0].canvas.before:
Color(.96, .96, .96, 1)
Rectangle(size=args[0].size, pos=args[0].pos)
# This function creates the UI if it is in the Favorites List
[docs]
def createFavoritesUI(self):
""" Creates Favorites List. Not functional as of now"""
pass
# This function when called shows the Information Box of the plugin, using csengine.uix.ItemInformationBox class
[docs]
def showPluginInfo(self, instance):
""" This function is called when the user clicks on the Info button. This function will then show a popup box with all the relevant information.
:param instance: The instance of the current PluginBase class
:type instance: PluginBase
:return: void
"""
ib = ItemInformationBox(name=self.name, desc=self.description, rating=self.getItemRating(),
total_votes=self.getTotalVotes(), plugin_path=str(self.plugin_path),
item_image=str(self.infoImagePath), on_open=self.openPlugin)
ib.show()
# This function will be reading the rating of the plugin and returning that value. The rating will be shown as stars
[docs]
def getItemRating(self):
""" This function is called to fetch the plugin item's rating information. This is currently hardcoded as no online rating system is available on the CamSkool website.
:return: A rating value
:rtype: float
"""
# Write Code to get the Current Item Rating from Web
return round((random.random() * 5), 1)
# This function returns the Total number of votes read for the current plugin
[docs]
def getTotalVotes(self):
""" This function is called to fetch the plugin item's votes information. This is currently hardcoded as no online voting system is available on the CamSkool website.
:return: A voting value
:rtype: int
"""
# Write code to get the Current Plugins total voters
return math.floor(random.random() * 1000)
# This is the function when called opens the plugin.
[docs]
def openPlugin(self, *args):
""" This function is called when the user clicks the "Open" button of the plugin or opens the plugin from anywhere. This function then adds the current plugin to the current stack in the current classroom.
:return: void
"""
res, new_plugin = ActivePlugin.active_slide.addToStack(self)
if not res:
NotificationUtility.notify(message="The stack only allows [b]3 items[/b]. Try removing an item and then add new.", auto_close=True)
return
new_plugin.loader = ShowWaiting()
new_plugin.loader.show(new_plugin.loaderText)
new_plugin.loader.bind(on_open=new_plugin.startPreload)
# This is the function that starts the preload() function in a separated thread and waits for it to complete.
[docs]
def startPreload(self, *args):
self.preloadThread = threading.Thread(target=self.preload)
self.preloadThread.start()
self.preloadClock = Clock.schedule_interval(self.isPreloadComplete, .2)
# This is the callback function which is called when the preload function is complete. DO NOT OVERRIDE
[docs]
def isPreloadComplete(self, dt):
if not self.preloadThread.is_alive():
self.preloadClock.cancel()
self.showController()
self.loader.close()
# Method that creates the base of the controller
## DO NOT OVERRIDE. Override modifyController() instead in the subclass.
[docs]
def showController(self, *args):
globals.AUDIO_THREAD.audioOutStateChange = self.onAudioMonitorReady # Register for callback when Audio is ready
self.controller = ModalView()
## GRID LAYOUT CANVAS THAT WILL BE THE PROVIDED FOR PLUGINS
self.controller.title = self.name
self.controller.separator_height = 0
self.controller.auto_dismiss = False
self.controller.size_hint_x = None
self.controller.size_hint_y = None
self.controller.size = (self.controllerWidth, self.controllerHeight)
self.controller.pos_hint = {'x': 0, 'y': -.6}
self.controller.lalign_flag = "left"
self.controller.background = str(self.controllerBG)
self.setWindowSize(self.appWidth, self.appHeight)
self.registerAudioLevelCallbacks()
if self.controllerCanvas:
self.restoreController()
return
self.controllerCanvas = GridLayout(cols=1, padding=('10sp','10sp'))
## ADD EXIT BUTTON
self.controllerCanvas.add_widget(self.createMenuButtons())
# self.controllerCanvas.add_widget(self.createMinimizeButton())
self.controllerCanvas.add_widget(Label(text=" ", size_hint= (1, .05)))
self.controllerGL = self.modifyController()
self.controllerCanvas.add_widget(self.controllerGL)
self.controller.open()
animate = Animation(pos_hint={"y": 0}, duration=.3, t="out_quad")
animate.start(self.controller)
## ADD STUFF AND SET DEFAULTS
self.controller.add_widget(self.controllerCanvas)
self.activatePlugin()
# This function is called when plugin already in stack is called.
[docs]
def restoreController(self, animate=True):
self.controller.open()
self.controller.add_widget(self.controllerCanvas)
self.activatePlugin()
if animate:
animate = Animation(pos_hint={"y": 0}, duration=.3, t="out_quad")
animate.start(self.controller)
# Make the Audio Button Update to the current Audio Status.
if globals.AUDIO_THREAD.getAudioMonitorState():
self.audioMonitorBtn.state = "normal"
else:
self.audioMonitorBtn.state = "down"
# Make the Favorite Icon as per the Favorites List
if self.originalInstance in PluginUtility.Plg_Favorites:
self.favoriteButtonCon.state = "down"
self.originalInstance.favoriteButton.opacity = 1
else:
self.favoriteButtonCon.state = "normal"
self.originalInstance.favoriteButton.opacity = 0
return
# This function puts the current plugin in the favorites Plugin List
[docs]
def addRemoveFavorites(self, instance):
DebugUtility.Debug(str(self.name) + " added to Favorites List")
if instance.state == "down":
self.originalInstance.favoriteButton.state = "down"
if self.originalInstance not in PluginUtility.Plg_Favorites:
PluginUtility.Plg_Favorites.append(self.originalInstance)
else:
self.originalInstance.favoriteButton.state = "normal"
if self.originalInstance in PluginUtility.Plg_Favorites:
PluginUtility.Plg_Favorites.remove(self.originalInstance)
PluginUtility.SaveFavoritesToFile() # Save favorites to file.
# This function closes the preloader loading gif.
[docs]
def closeLoader(self, *args):
self.loader.close()
## Method that creates the Exit Button for the controller.
## DO NOT MODIFY OR OVERRIDE
# This function is called when the AUdio Monitor Button is clicked
[docs]
def audioMonitorToggle(self, *args):
self.audioMonitorBtn = args[0]
args[0].disabled = True
if args[0].state == "normal":
globals.AUDIO_THREAD.setPlayback(True)
else:
globals.AUDIO_THREAD.setPlayback(False)
# This function is called when the AUdio Monitor is ready and connected.
[docs]
def onAudioMonitorReady(self, connected):
self.audioMonitorBtn.disabled = False
# This function is called when the controller is minimized
[docs]
def minimizeController(self, controller, animate=True):
# If the controller is minimized we want to save the gridlayout for when it will be restored.
# So we need to clear the popups children and make the GridLayout's parent as none. This is to save the layout
# for future use.
self.controller.clear_widgets()
self.controllerCanvas.parent = None
if animate:
animate = Animation(pos_hint={"y": -.6}, duration=.3, t="out_quad")
animate.start(self.controller)
controller.dismiss()
self.restoreWindowSize() # Called on close or minimize in case a plugin has modified the window size
# The following function activates the Plugin for Video Engine to start picking up feed from it.
# DO NOT OVERRIDE
[docs]
def activatePlugin(self):
# ActivePlugin.active_plugin = self
try:
self.play()
self.writerEvent.set()
except Exception as e:
DebugUtility.Err("Error while activating plugin: " + str(e), e)
# The following function deactivates the Plugin for Video Engine to stop picking up feed from it.
# DO NOT OVERRIDE
[docs]
def deactivatePlugin(self):
# ActivePlugin.active_plugin = None
self.writerEvent.clear()
ActivePlugin.active_slide.removeFromStack(self)
# This function is called when the controller close button is clicked.
# DO NOT OVERRIDE this function. If you want to do things on close use close() method
[docs]
def closeController(self, *args):
if self.confirmOnClose:
self.p = PopUpYESNO(title="Are You Sure",
message=self.confirmCloseText,
on_yes=self.accept,
on_no=self.reject,
icon_type=2,
size=("300sp", "200sp"))
self.p.show()
else:
self.accept()
# Function for Popup Close Confirmation NO. DO NOT OVERRIDE
# Function for Popup Close Confirmation YES.
# DO NOT OVERRIDE. IF YOU DON'T WANT POPUP ON EXIT set self.confirmOnClose to False
[docs]
def accept(self):
self.deactivatePlugin()
self.stop()
animate = Animation(pos_hint={"y": -.6}, duration=.2, t="in_quad")
animate.bind(on_complete=self.controller.dismiss)
animate.start(self.controller)
# This is an addition function to close the open plugin. Use this to close the plugin from code directly
# DO NOT OVERRIDE
[docs]
def pluginDismiss(self):
self.accept()
# This function checks if the current plugin is compatible with the current OS. marks the self.addFlag value
# if self.addFlag is False, the plugin will not show up in the list
# if self.addFlag is True only then it will show in the list. DO NOT OVERRIDE
# The following function adds the Plugin to the appropriate Lists
# DO NOT OVERRIDE
[docs]
def AddPluginToList(self):
self.ObservePlatformCompatibility()
if self.type == 'TOOL' and self.addFlag:
DebugUtility.Log("Adding Plugin : " + self.name + " to the Plg_Tool_lst")
PluginUtility.Plg_Tool_List.append(self)
elif self.type == 'ANIM' and self.addFlag:
DebugUtility.Log("Adding Plugin : " + self.name + " to the Plg_Anim_lst")
PluginUtility.Plg_Anim_List.append(self)
elif self.type == 'IMAGE' and self.addFlag:
DebugUtility.Log("Adding Plugin : " + self.name + " to the Plg_Image_lst")
PluginUtility.Plg_Img_List.append(self)
elif self.type == 'SOUND' and self.addFlag:
DebugUtility.Log("Adding Plugin : " + self.name + " to the Plg_Sound_lst")
PluginUtility.Plg_Sound_List.append(self)
else:
if self.addFlag:
DebugUtility.Log("Adding Plugin : " + self.name + " to the Plg_Tool_lst")
PluginUtility.Plg_Tool_List.append(self)
# If Plugin can be stacked
if self.stackable:
DebugUtility.Log("Adding Plugin : " + self.name + " to the Plg_SEffects_lst")
PluginUtility.Plg_SEffect_List.append(self)
########################################################
# Functions that can be called from the plugin code
#########################################################
# This method can be used to resize the app window
[docs]
def setWindowSize(self, x, y):
""" Call this function to resize the application window to the desired size with animation.
:param x: The new desired width
:type x: int
:param y: The new desired height
:type y: int
:return: void
"""
w = Window.size[0]
h = Window.size[1]
distance = 0
if abs(w - x) >= abs(h - y):
distance = abs(w - x)
else:
distance = abs(h - y)
time = distance/500
wanim = Animation(size=(x,y), duration=time, t="in_out_quad")
wanim.start(Window)
# Call this function to restore the app window size to regular 360x650
[docs]
def restoreWindowSize(self):
""" This function when called restores the window size from whatever the current window size is to the original window size with an animation
return: void
"""
w = Window.size[0]
h = Window.size[1]
distance = 0
if abs(w - 360) >= abs(h - 650):
distance = abs(w - 360)
else:
distance = abs(h - 650)
time = distance/500
wanim = Animation(size=(360, 650), duration=time, t="out_quad")
wanim.start(Window)
#####################################################################################################
# CORE AUDIO FUNCTIONS. DO NOT OVERRIDE.
#####################################################################################################
# This function plays the Audio file provided as Path or String. DO NOT OVerride in SUBCLASS
# In subclass only call this function using self.playAudioFile(filepath)
[docs]
def playAudioFile(self, filepath, *args):
csglobal.AUDIO_THREAD.playFile(str(filepath))
[docs]
def createAudioOutput(self, bufferSize = None):
pipe = csglobal.AUDIO_THREAD.createAudioOutput(bufferSize)
self.pipes.append(pipe)
return pipe
[docs]
def disconnectAudioPort(self, pipe):
csglobal.AUDIO_THREAD.disconnectPort(pipe)
[docs]
def getAudio(self, chunk):
return
[docs]
def writeAudioOverlay(self, chunk):
#block this Thread if plugin is inactive
self.writerEvent.wait()
#TODO: throw exception if closed!!!
if self.audioOverlayChannel:
self.audioOverlayChannel.write(chunk)
[docs]
def applyAudioChannelGain(self, gain):
nr = self.audioOverlayChannel.channelNr
globals.AUDIO_THREAD.applyChannelGain(nr, gain)
[docs]
def applyAudioMainGain(self, gain):
globals.AUDIO_THREAD.applyMasterChannelGain(gain)
[docs]
def registerAudioLevelCallbacks(self):
try:
self.getMainAudioLevel
globals.AUDIO_THREAD.OLM = self.getMainAudioLevel
except:
pass
try:
self.getChannelAudioLevel
if self.audioOverlayChannel:
nr = self.audioOverlayChannel.channelNr
if nr == 0:
globals.AUDIO_THREAD.OL0 = self.getChannelAudioLevel
if nr == 1:
globals.AUDIO_THREAD.OL1 = self.getChannelAudioLevel
if nr == 2:
globals.AUDIO_THREAD.OL2 = self.getChannelAudioLevel
except:
pass
###################################################################################################
### THE FOLLOWING FUNCTIONS CAN BE OVERRIDDEN TO ACHIEVE THE FUNCTIONALITY OR LOOK YOU LIKE.
###################################################################################################
# Override this function in subclass to modify the controller UI.
# If not overridden it will show an emtpy controller
# Use self.controllerCanvas.add_widget( YOUR MAIN WIDGET HERE)
[docs]
def modifyController(self):
""" Override this function in the child plugin class to modify how the controller looks. You can change the look and feel of the controller, add controls and functionality in this function.
:return: Returns the Kivy Parent Layout for the controller. Add controls and Listeners to the Plugin Controller and return the parent Layout.
:rtype: kivy.uix.gridlayout
"""
return GridLayout(cols=1)
#
[docs]
def getFrame(self, cam_frame):
""" This function is called every Frame. Override this function to change the frame to be returned every time the Video Engine calls this function.
Modify the frame by using openCV functions or pre-made function ins csengine.easycv.py and return the modified frame.
:return: Returns the modified frame.
:rtype: numpy.array image
"""
return cam_frame
# This function is called in case another frame is still being processes - so we drop one frame to hold the framerate
[docs]
def dropFrame(self):
"""This function is called in case another frame is still being processes - so we drop one frame to hold the frame rate
:return: void
"""
return
# This function is called immediately after the Controller is created and made visible.
# Add any code in the function override that needs to run when the controller is visible.
[docs]
def play(self):
""" This function is called immediately after the Controller is created and made visible.
Add any code in the function override that needs to run when the controller is visible.
This is called whenever the plugin is loaded or initialized.
:return: void
"""
pass
# This function is called when the controller is closed and the confirm dialog is also YES
# override to
[docs]
def stop(self):
""" This function is called when the controller is closed and the confirm dialog is also YES
:return: void
"""
pass
# Override this function to put anything that needs to be completed before the plugin opens.
[docs]
def preload(self):
""" Override this function to put anything that needs to be completed before the plugin opens.
:return: void
"""
pass
# END OF CLASS