Source code for csengine.emptypluginbase

"""
    @Name:                  Empty Plugin Parent Template
    @Description:           This is the Empty Plugin Class that will be parent class to empty plugins that can be
                            shipped with the application. These empty plugins lead you to the download page.
    @Created on:            Jan-2022
    @Created by:            Vinimay Kaul
    @Last Modified:         21-Jan-2022
    @Last Modified by:      Vinimay Kaul
"""

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

import webbrowser

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 EmptyPluginBase: ############################################################################################# # 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() ############################################################################################# """ Represents am Empty Plugin Base Template with its properties and methods. :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: :vartype infoImagePath: :ivar iconImage: :vartype iconImage: :ivar downloadLink: :vartype downloadLink: :ivar pipes: The pipes array used to communicate between modules. :vartype pipes: pipes Array :ivar writerEvent: A Thread Event used to write the Audio data :vartype writerEvent: threads.Event :ivar audioReRoute: Value defines if the Audio will be rerouted to the speakers or not :vartype audioReRoute: bool :ivar requiresRGB: This value determines if the frame needs to return the RGB value or RGBA. If true then frames will be returned as RGBA else RGB :vartype requiredRG: bool """ 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") self.downloadLink = kwargs.get("link", "https://camskool.com/store") 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, disabled=True) #tgl_fav.bind(on_press=self.addRemoveFavorites) btn_info = Button(text="", background_normal="./CommonMedia/info_button.png", background_down="./CommonMedia/info_button_D.png", disabled=True) btn_info.bind(on_release=self.showPluginInfo) btn_open = Button(size_hint=(None, None), width="85sp", height="28sp", text="Download", color=(0, 0, 0, 1), background_normal="./CommonMedia/open_button.png", background_down="./CommonMedia/open_button_D.png") btn_open.bind(on_release=self.downloadPlugin) 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) gL3.add_widget(Label()) gL2.add_widget(gL4) gL4.add_widget(Label()) gL2.add_widget(gL5) # gL5.add_widget(tgl_fav) gL5.add_widget(Label()) 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): with instance.canvas.before: Color(1, 1, 1, 1) Rectangle(source=str(self.normalBG), pos=(instance.x, instance.y), size=(instance.width, instance.height))
# Call back function for Mouse Over.
[docs] def onMouseOver(self, *args): 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): gL = GridLayout(cols=3, padding=["10sp", "0sp", "10sp", "0sp"], size_hint_y=None, height="50sp") gLlbl = GridLayout(cols=1, size_hint_y="50sp") gLBtn = GridLayout(cols=1, 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), disabled=True) #btnInfo.makeQuarterButton() btnOpen = RoundedButton(text="Download", on_release=self.downloadPlugin, background_color=(0, 0.447, 0.737, 1)) btnOpen.makeHalfButton() 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): 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): pass
# This function when called shows the Information Box of the plugin, using csengine.uix.ItemInformationBox class
[docs] def showPluginInfo(self, instance): 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): # 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): # Write code to get the Current Plugins total voters return math.floor(random.random() * 1000)
# 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
[docs] def ObservePlatformCompatibility(self): if self.platform == 0: self.addFlag = True elif self.platform == 1 and OSUtility.GetPlatformID() == 'WIN': self.addFlag = True elif self.platform == 2 and OSUtility.GetPlatformID() == 'MAC': self.addFlag = True else: self.addFlag = False
# 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)
## This method is responsible for opening the Web Browser and goto the image link
[docs] def downloadPlugin(self, *args): DebugUtility.Log("Downloading " + str(self.name) + " from " + str(self.downloadLink)) webbrowser.open(self.downloadLink)
# END OF CLASS