Source code for csengine.uix
"""
@Name: Cam Engine UI Elements
@Description: This file includes all the reusable UI Elements or UI elements that
the plugins need to access from within the application.
@Created on: Dec-2020
@Created by: Vinimay Kaul
@Last Modified: 26-Dec-2022
@Last Modified by: Vinimay Kaul
"""
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.screenmanager import Screen
from kivy.uix.popup import Popup
from kivy.uix.modalview import ModalView
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.image import Image
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.animation import Animation
from kivy.lang.builder import Builder
from kivy.uix.scrollview import ScrollView
from kivy.uix.textinput import TextInput
from kivy.uix.scatter import Scatter
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics.transformation import Matrix
from kivy.clock import Clock
import math, time, threading, os, sysinfo
from . import utility
from . import globals
from .utility import DebugUtility, FireBaseUtility, OSUtility, FileUtility, PluginUtility, NotificationUtility
from .globals import CEEnvironment, ActivePlugin
from .tkinter import TkinterAPI
from .slides import Slides
import sys, importlib
from pathlib import Path
#Class for Custom Message Popup
[docs]
class RPCustomMessagePopup(Popup):
message_popup = ObjectProperty(None)
message = ObjectProperty(None)
action = ""
button_caption = ObjectProperty(None)
caller = None
# Class for Custom Message Popup
[docs]
class RPMessagePopup(Popup):
message_popup = ObjectProperty(None)
message = ObjectProperty(None)
caller = None
# Class for Popup for validation
[docs]
class RPValidatePopup(Popup):
validate_popup = ObjectProperty(None)
message = ObjectProperty(None)
caller = None
# Class for NEW VALIDATION POPUP
# The Rewards Screen
# class for The Settings screen
[docs]
class ControlsScreen(Popup):
controller_popup = ObjectProperty(None)
game_name = ObjectProperty(None)
game_icon = ObjectProperty(None)
# Login Screen
[docs]
class LoginScreen(Screen):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
[docs]
def on_kv_post(self, base_widget): # this function is called after all KV has been applied. Kind of on_load function
print("ENTERED THE POST KV")
if (globals.USER_VALID and (globals.USER_PLAN > 0)):
self.manager.transition.direction = 'down'
self.manager.current = "menu"
# MainScreen
[docs]
class MainScreen(Screen):
screen_manager = ObjectProperty(None)
def __init__(self, sm=None, **kwargs):
super().__init__(**kwargs)
## ADD ANIMATIONS TO ANIMATION SCREEN
rewards_screen = self.ids["menu_screen"].ids["rewards_screen"].ids["gLayout"] # Locate the GridLayout to add
globals.ApplicationScreens.SetAnimationsScreen(rewards_screen)
## ADD ITEMS TO TOOLS SCREEN
tools_screen = self.ids["menu_screen"].ids["tools_screen"].ids["gLayout"] # Locate the GridLayout to add
globals.ApplicationScreens.SetToolsScreen(tools_screen)
## ADD ITEMS TO PLAY & SHOW SCREEN
pns_screen = self.ids["menu_screen"].ids["showntell_screen"].ids["gLayout"] # Locate the GridLayout to add
globals.ApplicationScreens.SetPnSScreen(pns_screen)
search_screen = self.ids["search_screen"].ids["gLayout"] # Locate the GridLayout to add
globals.ApplicationScreens.SetSearchScreen(search_screen)
MenuScreen.LoadAllPlugins() # Now Start Loading and Adding Plugins to Screens.
# class for The Show & Tell screen
# class for The Tools screen
#######################################################################
# SEARCH SCREEN CLASS
#######################################################################
[docs]
class SearchScreen(Screen):
[docs]
def search(self, keywords):
globals.ApplicationScreens.GetSearchScreen().clear_widgets()
weightedList = []
result, weight = PluginUtility.searchUtility(keywords, "all")
idx = 0
for item in result:
# item.ObservePlatformCompatibility()
if item.addFlag:
weightedList.append([item, weight[idx]])
idx += 1
weightedList.sort(key=self.sortByWeight, reverse=True)
if len(weightedList) == 0:
globals.ApplicationScreens.GetSearchScreen().add_widget(Label(text="[color=#aaaaaa][i]No matches found[/i][/color]", markup=True))
return
for plugin in weightedList:
globals.ApplicationScreens.GetSearchScreen().add_widget(plugin[0].createSearchListUI())
# globals.ApplicationScreens.GetSearchScreen().clear_widgets()
# MenuScreen.RefreshPlugins()
#######################################################################
# MAIN MENU SCREEN CLASS
#######################################################################
[docs]
class SideMenuScreen(Screen):
[docs]
def showUpcoming(self, item):
p = PopUpOK(title="Upcoming Feature",
message="The " + item + " Feature is coming soon and is not available in this version.",
icon_type=1, size=("300sp", "200sp"))
p.show()
[docs]
def reportBug(self, instance):
ipop = Popup()
ipop.title = "Report Issue"
ipop.title_color = (.2, .2, .2, 1)
ipop.size_hint = (None, None)
ipop.size = ("340sp", "500sp")
ipop.pos_hint = {"y": .1}
ipop.background = "./CommonMedia/message_box_background.png"
gl1 = GridLayout(cols=1, padding=["10sp", "10sp", "10sp", "10sp"])
gl2 = GridLayout(cols=3, size_hint_y=.15)
# message = Label(text="Describe the problems you faced while using our software. We will try and look into it and try and solve the problem as soon as possible", size_hint_y=.2)
labelKV = """
Label:
text: "Describe the problems you faced while using our software. Please be as descriptive as possible. "
text_size: self.size
size_hint_y: .2
valign: "top"
"""
message = Builder.load_string(labelKV)
ti = TextInput(multiline=True, size_hint_y=.7)
btnSubmit = RoundedButton(text="Submit")
btnSubmit.makeHalfButton()
btnCancel = RoundedButton(text="Cancel")
btnCancel.makeHalfButton()
btnSubmit.color = (1, 1, 1, 1)
btnSubmit.background_color = (0, 0.447, 0.737, 1)
btnCancel.color = (1, 1, 1, 1)
btnCancel.background_color = (1, .506, 0.07, 1)
btnCancel.bind(on_release=ipop.dismiss)
btnSubmit.bind(on_release=lambda x: self.submitReport(ipop, ti))
gl2.add_widget(btnCancel)
gl2.add_widget(Label())
gl2.add_widget(btnSubmit)
gl1.add_widget(message)
gl1.add_widget(ti)
gl1.add_widget(gl2)
ipop.add_widget(gl1)
ipop.open()
animate = Animation(pos_hint={"y": .13}, opacity=1, duration=.2, t="out_quad")
animate.start(ipop)
[docs]
def submitReport(self, ipop, ti, *args):
userDesc = ti.text
ipop.dismiss()
savePath = "/tmp/"
if OSUtility.GetPlatformID() == "WIN":
savePath = str(CEEnvironment.GetAppDataFolder())
FileUtility.SaveToFile(savePath, "userReport.txt", str(userDesc), append=False)
self.uploadBug()
[docs]
def uploadBug(self, *args):
if DebugUtility.frozen:
DebugUtility.Debug("Running from Executable")
os.system("CSDownloader.exe CAMSKOOL?reportissue")
else:
DebugUtility.Debug("Running from Python Environment")
sysinfo.log_sysinfo()
os.system("python CSDownloader.py CAMSKOOL?reportissue")
pass
[docs]
def showHideSideMenu(self):
return
if self.sidebarMenuGL.state:
self.sidebarMenuGL.state = False
anim = Animation(pos=(360+180, 0), opacity=0, duration=.3, transition="in_quad")
# anim &= Animation(opacity=0, duration=.5)
anim.start(self.sidebarMenuGL)
else:
self.sidebarMenuGL.state = True
# anim = Animation(opacity=1, duration=.5)
anim = Animation(pos=(360-149, 0), opacity=1, duration=.3, transition="out_quad")
anim.start(self.sidebarMenuGL)
[docs]
class MenuScreen(Screen):
settings_btn = StringProperty("./CommonMedia//settings.png")
settings_btn_D = StringProperty("./CommonMedia//settings_D.png")
help_btn = StringProperty("./CommonMedia//help.png")
help_btn_D = StringProperty("./CommonMedia//help_D.png")
rewards_btn = StringProperty("./CommonMedia//rewards.png")
rewards_btn_D = StringProperty("./CommonMedia//rewards_D.png")
play_btn = StringProperty("./CommonMedia//play.png")
play_btn_D = StringProperty("./CommonMedia//play_D.png")
tools_btn = StringProperty("./CommonMedia//tools.png")
tools_btn_D = StringProperty("./CommonMedia//tools_D.png")
search_btn = StringProperty("./CommonMedia//search.png")
search_btn_D = StringProperty("./CommonMedia//search_D.png")
sidebarMenuGL = ObjectProperty(None)
[docs]
def on_enter(self):
self.stackName = self.ids["classroomName"]
self.stackButton1 = self.ids["stackItem1"]
self.stackButton2 = self.ids["stackItem2"]
self.stackButton3 = self.ids["stackItem3"]
print(self.stackName.text)
globals.STACK_BUTTONS.append(self.stackButton1)
globals.STACK_BUTTONS.append(self.stackButton2)
globals.STACK_BUTTONS.append(self.stackButton3)
globals.ACTIVE_SLIDE_NAME = self.stackName
[docs]
def showUpcoming(self, item):
p = PopUpOK(title="Upcoming Feature",
message="The " + item + " Feature is coming soon and is not available in this version.",
icon_type=1, size=("300sp", "200sp"))
p.show()
[docs]
@staticmethod
def LoadAllPlugins():
# APPEND PLUGINS FOLDER PATH TO THE SYSTEM PATH FOR IMPORT
DebugUtility.Log("##################################################")
DebugUtility.Log("Adding Environment to Import Plugins")
DebugUtility.Log("Sys.Path Count Before Adding :: " + str(len(sys.path)))
DebugUtility.Log("App Data Folder's Current Value :: " + str(CEEnvironment.GetAppDataFolder()))
sys.path.append(CEEnvironment.GetAppDataFolder() / "PluginsCE")
DebugUtility.Log("Sys.Path Count After Adding :: " + str(len(sys.path)))
DebugUtility.Log("##################################################")
for pth in sys.path:
DebugUtility.Log(str(pth))
try:
plugins_path = str(CEEnvironment.GetAppDataFolder() / "PluginsCE" / "__init__.py")
pkg_name = "PluginsCE"
spec = importlib.util.spec_from_file_location(pkg_name, plugins_path)
PluginsCE = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = PluginsCE
spec.loader.exec_module(PluginsCE)
# PluginsCE = importlib.import_module('PluginsCE')
# PluginsCE.__init__
# # Find all plugins before the app runs
utility.PluginUtility.GetAllPlugins(PluginsCE)
# # Run all plugins so that they are added to the list
#ce = CryptoEngine() # NOT USED HERE ANYMORE. USE GLOBAL OBJECT INSTEAD
utility.PluginUtility.ActivatePlugins()
except Exception as e:
DebugUtility.Err("EXCEPTION LOADING PLUGINS", DebugUtility.InspectFrame())
DebugUtility.Log(str(e))
else:
DebugUtility.Log("##################################################")
DebugUtility.Log("Importing PluginsCE")
MenuScreen.RefreshPlugins()
[docs]
@staticmethod
def RefreshPlugins(refresh=False):
# for each plugin found in Plugins_Animation_list
DebugUtility.Log("##################################################")
DebugUtility.Log("Sorting Plugins")
DebugUtility.Log("Items is Animations : " + str(len(utility.PluginUtility.Plg_Anim_List)))
DebugUtility.Log("Items is Play & Show : " + str(len(utility.PluginUtility.Plg_Img_List)))
DebugUtility.Log("Items is Tools : " + str(len(utility.PluginUtility.Plg_Tool_List)))
if refresh:
globals.ApplicationScreens.GetAnimationsScreen().clear_widgets()
globals.ApplicationScreens.GetToolsScreen().clear_widgets()
globals.ApplicationScreens.GetPnSScreen().clear_widgets()
for item in utility.PluginUtility.Plg_Anim_List:
utility.DebugUtility.Log("Adding Plugin " + str(item.name) + " to Animations/Rewards")
globals.ApplicationScreens.GetAnimationsScreen().add_widget(item.createPluginUI()) # add them to the rewardPanel
for item in utility.PluginUtility.Plg_Tool_List:
utility.DebugUtility.Log("Adding Plugin " + str(item.name) + " to Tools")
globals.ApplicationScreens.GetToolsScreen().add_widget(item.createPluginUI()) # add them to the rewardPanel
for item in utility.PluginUtility.Plg_Img_List:
utility.DebugUtility.Log("Adding Plugin " + str(item.name) + " to Play & Show")
globals.ApplicationScreens.GetPnSScreen().add_widget(item.createPluginUI()) # add them to the rewardPanel
DebugUtility.Log("All Plugins Sorted!")
## THE ABOVE WOULD REPEAT FOR ALL SECTIONS OR SCREENS
# LOAD INITIAL SLIDE
ActivePlugin.slideObject = Slides()
[docs]
def stackButtonClicked(self, instance, index):
ctr = 0
for items in globals.STACK_BUTTONS:
if instance == globals.STACK_BUTTONS[ctr]:
globals.ActivePlugin.active_slide.openSelectedController(ctr)
ctr += 1
#######################################################################
# SLIDE SCREEN CLASS
#######################################################################
[docs]
class SlideScreen(Screen):
firstEnter = True
slideLimit = 18
# def __init__(self, *args, **kwargs):
# super(self, SlideScreen).__init__(*args, **kwargs)
# self.activeButton = None
[docs]
def on_enter(self):
self.activeButton = self.ids["slide1"]
if SlideScreen.firstEnter:
self.slideButtons = []
# self.activeSlide = 0
for i in range(1, SlideScreen.slideLimit + 1):
btn = self.ids["slide" + str(i)]
self.slideButtons.append(btn)
if not btn.disabled and btn not in globals.ENABLED_SLIDES:
globals.ENABLED_SLIDES.append(btn)
SlideScreen.firstEnter = False
self.stackNameSL = self.ids["classroomNameSL"]
self.stackButtonSL1 = self.ids["stackItemSL1"]
self.stackButtonSL2 = self.ids["stackItemSL2"]
self.stackButtonSL3 = self.ids["stackItemSL3"]
globals.STACK_BUTTONS_SL.append(self.stackButtonSL1)
globals.STACK_BUTTONS_SL.append(self.stackButtonSL2)
globals.STACK_BUTTONS_SL.append(self.stackButtonSL3)
globals.ACTIVE_SLIDE_NAME_SL = self.stackNameSL
globals.ActivePlugin.active_slide.updateStackButtons()
[docs]
def onSlideClicked(self, btn_Instance, index):
globals.ACTIVE_SLIDE_INDEX = index
globals.ActivePlugin.slideObject.makeSelectionActive(index)
self.activeButton = btn_Instance
# self.parent.current = "menu"
[docs]
def addSlide(self):
ctr = len(globals.ENABLED_SLIDES)
if ctr == SlideScreen.slideLimit:
return False
self.slideButtons[ctr].disabled = False
self.slideButtons[ctr].text = "Classroom " + str(ctr + 1)
globals.ENABLED_SLIDES.append(self.slideButtons[ctr])
self.enableButton(ctr)
self.activeButton = self.slideButtons[ctr]
ActivePlugin.slideObject.addSlide()
return True
[docs]
def removeSlide(self):
if globals.ACTIVE_SLIDE_INDEX == 0 and len(globals.ENABLED_SLIDES) == 1:
NotificationUtility.notify(message="At lease one slide has to be present.")
else:
PopUpYESNO(title="Remove Slide?",
message="Are you sure you want to delete this slide? This cannot be undone!",
icon_type=2, on_yes=self.definiteDelete, size=(320,220)).show()
[docs]
def definiteDelete(self):
ActivePlugin.slideObject.removeSlide(globals.ACTIVE_SLIDE_INDEX)
del globals.ENABLED_SLIDES[globals.ACTIVE_SLIDE_INDEX]
self.rebuildSlides(len(globals.ENABLED_SLIDES) - 1)
[docs]
def enableButton(self, index):
for btn in self.slideButtons:
btn.state = "normal"
self.slideButtons[index].state = "down"
globals.ACTIVE_SLIDE_INDEX = index
[docs]
def rebuildSlides(self, activeIndex):
for btn in self.slideButtons:
btn.state = "normal"
btn.disabled = True
btn.text = ""
for i in range(0, len(globals.ENABLED_SLIDES)):
self.slideButtons[i].disabled = False
if ActivePlugin.slideObject.slidesList[i].customName:
self.slideButtons[i].text = ActivePlugin.slideObject.slidesList[i].name
else:
self.slideButtons[i].text = "Classroom " + str(i + 1)
ActivePlugin.slideObject.slidesList[i].name = "Classroom " + str(i + 1)
self.enableButton(activeIndex)
ActivePlugin.slideObject.makeSelectionActive(activeIndex)
[docs]
def stackButtonClicked(self, instance, index):
# self.parent.transition.direction = 'down'
# self.parent.current = 'menu'
ctr = 0
for items in globals.STACK_BUTTONS:
if instance == globals.STACK_BUTTONS_SL[ctr]:
globals.ActivePlugin.active_slide.openSelectedController(ctr)
ctr += 1
# Function to change the name of the Classroom
##################################################################
"""
@brief The following classes are custom
UI elements that can be reused for
your plugins.
@author Vinimay Kaul
"""
##################################################################
[docs]
class RoundedButton(Button):
def __init__(self, **kwargs):
super().__init__(**kwargs)
[docs]
def makeFullButton(self):
self.background_normal = str(Path("./CommonMedia/round_button_300x36.png"))
self.background_down = str(Path("./CommonMedia/round_button_300x36_D.png"))
self.background_disabled_normal = str(Path("./CommonMedia/round_button_300x36_Dis.png"))
self.background_disabled_down = str(Path("./CommonMedia/round_button_300x36_Dis.png"))
# self.bind(on_press=kwargs.get("on_press", self.emptyFuntion))
# self.bind(on_release=kwargs.get("on_release", self.emptyFuntion))
self.size_hint = (None, None)
self.size = ("300sp", "36sp")
return self
[docs]
def makeHalfButton(self):
self.background_normal = str(Path("./CommonMedia/round_button_140x36.png"))
self.background_down = str(Path("./CommonMedia/round_button_140x36_D.png"))
self.background_disabled_normal = str(Path("./CommonMedia/round_button_140x36_Dis.png"))
self.background_disabled_down = str(Path("./CommonMedia/round_button_140x36_Dis.png"))
# self.bind(on_press=kwargs.get("on_press", self.emptyFuntion))
# self.bind(on_release=kwargs.get("on_release", self.emptyFuntion))
self.size_hint = (None, None)
self.size = ("140sp", "36sp")
return self
[docs]
def makeQuarterButton(self):
self.background_normal = str(Path("./CommonMedia/round_button_65x36.png"))
self.background_down = str(Path("./CommonMedia/round_button_65x36_D.png"))
self.background_disabled_normal = str(Path("./CommonMedia/round_button_65x36_Dis.png"))
self.background_disabled_down = str(Path("./CommonMedia/round_button_65x36_Dis.png"))
# self.bind(on_press=kwargs.get("on_press", self.emptyFuntion))
# self.bind(on_release=kwargs.get("on_release", self.emptyFuntion))
self.size_hint = (None, None)
self.size = ("65sp", "36sp")
###################################################################################################################
###################################################################################################################
## Custom Popups
[docs]
class PopUpOK(Popup):
# @brief This class of Popup can be used to create any alert inside the Application Window
# the Popup will have one message and one OK button.
# @parameter title as string
# @parameter message as string
# @parameter size as tuple (width, height)
# @parameter give icon type as 0 for No Icon, 1 for Info, 2 for Warning, 3 for Error
# @author Vinimay Kaul
# Comstructor
def __init__(self, **kwargs):
self.title = kwargs.get("title", "Alert")
self.message = kwargs.get("message", "This is an Alert")
self.size = kwargs.get("size", ("300sp", "200sp"))
self.iconType = kwargs.get("icon_type", 0)
if self.iconType == 1:
self.iconPath = "./CommonMedia/infoIcon.png"
elif self.iconType == 2:
self.iconPath = "./CommonMedia/warningIcon.png"
elif self.iconType == 3:
self.iconPath = "./CommonMedia/errorIcon.png"
else:
self.iconPath = ""
if not self.iconType == 0:
self.size = (self.size[0], self.size[1] + 50.0)
self.popupObject = None
# Function that will open PopUp
[docs]
def show(self):
pop = Popup()
self.popupObject = pop
pop.title = self.title
pop.size_hint = (None, None)
pop.size = self.size
pop.opacity = 0
pop.background = "./CommonMedia/message_box_background.png"
pop.title_color = (.1, .1, .1, 1)
pop.overlay_color = (0,0,0,0)
gl1 = GridLayout(cols=1)
gl3 = GridLayout(cols=3)
btn = RoundedButton(text="OK", on_press=self.closePop)
btn.color = (1, 1, 1, 1)
btn.background_color = (0, 0.447, 0.737, 1)
btn.makeHalfButton()
if not self.iconPath == "":
icon = Image(source=self.iconPath, width="60sp", height="60sp")
gl1.add_widget(Label(size_hint_y=None, height="10sp"))
gl1.add_widget(icon)
gl1.add_widget(Label(size_hint_y=None, height="10sp"))
# gl1.add_widget(Label(text=self.message, halign="center", valign="middle"))
messageKV = """
Label:
text: "{message}"
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
valign: "middle"
halign: "center"
markup: True
"""
messageKV = messageKV.format(message=self.message.replace("\n", "\\n"))
lbl_message = Builder.load_string(messageKV)
gl1.add_widget(lbl_message)
gl3.add_widget(Label())
gl3.add_widget(btn)
gl3.add_widget(Label())
gl1.add_widget(gl3)
pop.add_widget(gl1)
pop.open()
pop.pos_hint = {"y": .3}
pop.opacity = 0
animate = Animation(pos_hint={"y":.33}, overlay_color=(0,0,0,.7), opacity=1, duration=.2, t="out_quad")
animate.start(pop)
return 0
[docs]
def closePop(self, *args):
animate = Animation(pos_hint={"y": .30}, overlay_color=(0, 0, 0, 0), opacity=0, duration=.2, t="out_quad")
animate.start(self.popupObject)
animate.bind(on_complete=self.popupObject.dismiss)
###################################################################################################################
[docs]
class PopUpYESNO(Popup):
# @brief This class of Popup can be used to create any Warning inside the Application Window
# the Popup will have one message and one YES button and one NO button. This will have
# an Warning Icon.
# @parameter title as string
# @parameter message as string
# @parameter size as tuple (width, height)
# @parameter on_yes a function that needs to be called when Yes is clicked
# @parameter on_no a function that needs to be called when No is clicked
# @parameter give icon type as 0 for No Icon, 1 for Info, 2 for Warning, 3 for Error
# @author Vinimay Kaul
# Comstructor
def __init__(self, **kwargs):
super().__init__()
self.title = kwargs.get("title", "Alert")
self.message = kwargs.get("message", "This is an Alert")
self.size = kwargs.get("size", ("340sp", "200sp"))
self.onYes = kwargs.get("on_yes", self.emptyDef)
self.onNo = kwargs.get("on_no", self.emptyDef)
self.iconType = kwargs.get("icon_type", 0)
if self.iconType == 1:
self.iconPath = "./CommonMedia/infoIcon.png"
elif self.iconType == 2:
self.iconPath = "./CommonMedia/warningIcon.png"
elif self.iconType == 3:
self.iconPath = "./CommonMedia/errorIcon.png"
else:
self.iconPath = ""
if not self.iconType == 0:
self.size = (self.size[0], self.size[1] + 50.0)
self.popupObject = None
# Function that will open PopUp
[docs]
def show(self):
# self.popupObject = None
pop = Popup()
self.popupObject = pop
pop.title = self.title
pop.size_hint = (None, None)
pop.size = self.size
pop.background = "./CommonMedia/message_box_background.png"
pop.title_color = (.1, .1, .1, 1)
pop.overlay_color = (0, 0, 0, 0)
gl1 = GridLayout(cols=1)
gl5 = GridLayout(cols=5)
btnY = RoundedButton(text="Yes")
btnY.color = (1,1,1,1)
btnY.background_color = (0, 0.447, 0.737, 1)
btnY.makeHalfButton()
btnN = RoundedButton(text="No")
btnN.background_color = (1,.506,0.07,1)
btnN.color = (1, 1, 1, 1)
btnN.makeHalfButton()
# btnY.bind(on_release=lambda x: self.onYesClicked(pop))
# btnN.bind(on_release=lambda x: self.onNoClicked(pop))
btnY.bind(on_press=self.onYesClicked)
btnN.bind(on_press=self.onNoClicked)
if not self.iconPath == "":
icon = Image(source=self.iconPath, width="60sp", height="60sp")
gl1.add_widget(Label(size_hint_y=None, height="10sp"))
gl1.add_widget(icon)
gl1.add_widget(Label(size_hint_y=None, height="10sp"))
# gl1.add_widget(Label(text=self.message, halign="center", valign="middle"))
messageKV = """
Label:
text: "{message}"
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
valign: "middle"
halign: "center"
markup: True
"""
messageKV = messageKV.format(message=self.message.replace("\n", "\\n"))
lbl_message = Builder.load_string(messageKV)
gl1.add_widget(lbl_message)
gl5.add_widget(Label())
gl5.add_widget(btnY)
gl5.add_widget(Label())
gl5.add_widget(btnN)
gl5.add_widget(Label())
gl1.add_widget(gl5)
pop.add_widget(gl1)
pop.open()
pop.pos_hint = {"y": .3}
pop.opacity = 0
animate = Animation(pos_hint={"y":.33}, overlay_color=(0,0,0,.7), opacity=1, duration=.2, t="out_quad")
animate.start(pop)
[docs]
def onYesClicked(self, instance):
self.onYes()
# instance.dismiss()
animate = Animation(pos_hint={"y": .30}, overlay_color=(0,0,0,0), opacity=0, duration=.2, t="out_quad")
animate.start(self.popupObject)
self.popupObject.dismiss()
[docs]
def onNoClicked(self, instance):
self.onNo()
animate = Animation(pos_hint={"y": .30}, overlay_color=(0,0,0,0), opacity=0, duration=.2, t="out_quad")
animate.start(self.popupObject)
self.popupObject.dismiss()
###########################################################
# Class for Notification
###########################################################
[docs]
class Notification(FloatLayout):
messageLabel = StringProperty("No Message")
notiLayout = ObjectProperty(None)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
NotificationUtility.NotificationObject = self
[docs]
def notify(self, text, auto_close=True):
self.notiLayout.pos_hint = {"center_y": .92}
anim = Animation(pos_hint={"center_y": .9}, opacity=1, duration=.1)
if auto_close:
anim.bind(on_complete=self.autoClose)
self.messageLabel = text
anim.start(self.notiLayout)
[docs]
def closeNotification(self, *args):
anim = Animation(pos_hint={"center_y": .92}, opacity=0, duration=.1)
anim.bind(on_complete=self.resetNotification)
anim.start(self.notiLayout)
[docs]
def resetNotification(self, *args):
self.notiLayout.opacity = 0
self.notiLayout.pos_hint = {"center_y": -.2}
self.messageLabel = "There is nothing to say."
###########################################################
# CLASS TO SHOW INFO POPUP
###########################################################
[docs]
class ItemInformationBox(Popup):
def __init__(self, **kwargs):
self.itemImage = kwargs.get("item_image", "./CommonMedia/sampleInfo.png")
self.itemName = kwargs.get("name", "Plugin")
self.itemDesc = kwargs.get("desc", "The Plugin description is not available.")
self.itemRating = kwargs.get("rating", 0)
self.totalVotes = kwargs.get("total_votes", 0)
self.itemPath = kwargs.get("plugin_path", "")
self.callbackOpen = kwargs.get("on_open", self.emptyDef)
if self.itemRating > 5:
self.itemRating = 5
self.InfoPopupObject = None
[docs]
def show(self):
# pop = Popup()
pop = ModalView()
self.InfoPopupObject = pop
pop.size_hint = (None, None)
pop.size = ("328", "480sp")
pop.background_color = (1,1,1,1)
pop.background = "./CommonMedia/infoBoxBG.png"
pop.title = ""
pop.separator_height = 0
pop.overlay_color = (0, 0, 0, 0)
gLOuter = GridLayout(cols=1)
glInner = GridLayout(cols=1)
glRating = GridLayout(cols=5, size_hint_y=None, height="30sp")
glButtons = GridLayout(cols=3)
glName = GridLayout(cols=1, padding=["10sp", 0 ,0 ,0])
gLOuter.padding = [0, "-30sp", 0 ,0]
glInner.padding = ["24sp", "-24sp", "24sp", 0]
flCloseBtn = FloatLayout(cols=1, size_hint=(0,0), opacity=0)
btnClose = Button(background_normal="./CommonMedia/ctrl_btn_exit.png",
background_down="./CommonMedia/ctrl_btn_exit_D.png",
size_hint=(None, None),
width="30sp",
height="30sp",
pos=(300,515),
on_release=self.closeInfo)
flCloseBtn.add_widget(btnClose)
image = Image(source=self.itemImage)
lbl_Name = Label(text="[size=20][color=#ff8213][b]" + self.itemName + "[/b][/color][/size]",
markup=True, halign="left",
valign="middle",
size_hint=(None, None))
lbl_Name.size = ("300sp", "30sp")
lbl_Name.text_size = (lbl_Name.width, 20)
self.itemDesc = "[size=14][color=#444444]" + self.itemDesc + "[/color][/size]"
descKV = self.getDescKV().format(itemDesc=self.itemDesc)
scrll = Builder.load_string(descKV)
# scrll = self.getInfoScrollView(itemDesc=self.itemDesc)
lbl_Rating = Label(text="[size=18][b]" + str(self.itemRating) + "[/b][/size]", valign="middle", halign="left", markup=True)
lbl_Rating.size_hint = (None, None)
lbl_Rating.width = "32sp"
lbl_Rating.height = "18sp"
lbl_Votes = Label(text="(" + str(self.totalVotes) + " votes)", valign="middle", halign="center")
lbl_Votes.size_hint = (None, None)
lbl_Votes.width = "100sp"
lbl_Votes.height = "18sp"
btn_Uninstall = RoundedButton(text="Uninstall", on_release=self.uninstallPlugin)
btn_Uninstall.color = (1,1,1,1)
btn_Uninstall.background_color = (0, 0.447, 0.737, 1)
btn_Uninstall.disabled = True
btn_Uninstall.makeHalfButton()
if self.itemPath == "":
btn_Uninstall.disabled = True
btn_Open = RoundedButton(text="Open", on_release=self.onOpen)
btn_Open.color = (1, 1, 1, 1)
btn_Open.background_color = (1, .506, 0.07, 1)
btn_Open.makeHalfButton()
glName.add_widget(lbl_Name)
glInner.add_widget(glName)
glInner.add_widget(Label(size_hint_y=None, height="10sp"))
glInner.add_widget(scrll)
glInner.add_widget(Label(size_hint_y=None, height="50sp"))
glInner.add_widget(Builder.load_string(self.getSeparator()))
# glInner.add_widget(Label(size_hint_y=None, height="10sp"))
# Add Separator here
glRating.add_widget(lbl_Rating)
glRating.add_widget(Builder.load_string(self.makeRatingStars(self.itemRating)))
glRating.add_widget(lbl_Votes)
glRating.add_widget(Label(size_hint_x= .3))
glButtons.add_widget(btn_Uninstall)
glButtons.add_widget(btn_Open)
glInner.add_widget(glRating)
glInner.add_widget(Label(size_hint_y=None, height="10sp"))
glInner.add_widget(glButtons)
gLOuter.add_widget(image)
gLOuter.add_widget(glInner)
gLOuter.add_widget(flCloseBtn)
pop.add_widget(gLOuter)
# pop.pos_hint = {"y": .15}
pop.pos_hint = {"y": .13}
pop.opacity = 0
pop.open()
def showCloseBtn(*args):
flCloseBtn.opacity = 1
animate = Animation(pos_hint={"y": .15}, overlay_color=(0,0,0,.7), opacity=1, duration=.2, t="out_quad")
animate.bind(on_complete=showCloseBtn)
animate.start(pop)
[docs]
def closeInfo(self, *args):
animate = Animation(pos_hint={"y": .13}, overlay_color=(0, 0, 0, 0), opacity=0, duration=.2, t="out_quad")
animate.bind(on_complete=self.InfoPopupObject.dismiss)
animate.start(self.InfoPopupObject)
# Perform Uninstall of the plugin
[docs]
def emptyDef(self, *args, **kwargs):
p = PopUpOK(title="Error",
message="Error Opening Item",
icon_type=3, size=('300sp', "200sp"))
p.show()
[docs]
def getDescKV(self):
kv = """
GridLayout:
cols: 1
padding: [0, dp(-30), 0, 0]
ScrollView:
do_scroll_x: False
do_scroll_y: True
size_hint_y: None
height: dp(130)
bar_inactive_color: (.7, .7, .7, .6)
bar_width: dp(8)
Label:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
padding: 10, 10
markup: True
text: "{itemDesc}"
"""
return kv
[docs]
def getInfoScrollView(self, itemDesc):
gl = GridLayout(cols=1, padding=[0, "-30sp", 0, 0])
sv = ScrollView(do_scroll_x=False, do_scroll_y=True, size_hint_y=None, height="130sp", bar_width=8,
bar_inactive_color = (.7,.7,.7,.6))
kv = """
Label:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
padding: 10, 10
markup: True
text: "{itemDesc}"
"""
kv = kv.format(itemDesc=itemDesc)
lbl = Builder.load_string(kv)
sv.add_widget(lbl)
gl.add_widget(sv)
sv.scroll_y = 1
# anim = Animation(scroll_y=1, duration=.3)
# anim.start(sv)
return gl
[docs]
def getSeparator(self):
kv = """
Label:
size_hint_y: None
height: dp(2)
canvas.before:
Color:
rgba: (.8, .8, .8, 1)
Rectangle:
pos: self.pos
size: self.size
"""
return kv
[docs]
def getStarImage(self, value):
starPath = 0
if value == 0:
starPath = "./CommonMedia/starEmpty.png"
elif value == 1:
starPath = "./CommonMedia/starFull.png"
else:
starPath = "./CommonMedia/starHalf.png"
kv = """
Image:
source: "{path}"
size_hint: (None, None)
width: dp(18)
height: dp(18)
"""
kv = kv.format(path=starPath)
return kv
[docs]
def makeRatingStars(self, value):
if value > 5:
value = 5
fullVal = math.floor(value)
ctr = 0
fullStarKV = """
GridLayout:
cols: 5
size_hint_y: None
padding: [0,0,dp(80),0]
height: dp(18)
"""
while ctr < fullVal:
fullStarKV += self.getStarImage(1)
ctr += 1
if value > 0:
diff = 5 - math.ceil(value)
if fullVal < value:
fullStarKV += self.getStarImage(.5)
# else:
# fullStarKV += self.getStarImage(0)
else:
diff = 5
ctr = 0
while ctr < diff:
fullStarKV += self.getStarImage(0)
ctr += 1
return fullStarKV
###########################################################
# Input Text Modal for changing Slide Name
###########################################################
[docs]
class InputTextDialog(ModalView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.callback = None
[docs]
def show(self, callback):
self.callback = callback
glMain = GridLayout(cols=2)
ti = TextInput(size_hint_y=None, height="30sp", multiline=False, hint_text="Enter Classroom Name (Max 20 chars)")
ti.bind(on_text_validate=self.onEnter)
glMain.add_widget(ti)
self.add_widget(glMain)
self.size_hint = (None, None)
self.size = ("300sp", "36sp")
self.pos_hint = {"y": .93}
self.open()
ti.focus = True
[docs]
def onEnter(self, *args):
newName = args[0].text[:20]
globals.ACTIVE_SLIDE_NAME.text = newName
globals.ACTIVE_SLIDE_NAME_SL.text = newName
ActivePlugin.active_slide.name = newName
ActivePlugin.active_slide.customName = True
self.callback(newName)
args[0].text = ""
self.dismiss()
#######################
# Loading ModalView
#######################
PluginLoader = None
[docs]
class ShowWaiting(ModalView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self.createLoader()
[docs]
def createLoader(self, text):
imgKV = """
GridLayout:
cols: 1
Label:
text: "{text}"
color: (1,1,1,1)
halign: "center"
Image:
id: gif
source: "./CommonMedia/loadingGIF.gif"
center: self.parent.center
size: 500, 500
allow_stretch: True
anim_delay: .05
anim_loop: 100
"""
imgKV = imgKV.format(text=text)
img = Builder.load_string(imgKV)
self.add_widget(img)
self.background_color = (0, 0, 0, 0)
self.overlay_color = (0,0,0,.8)
self.size_hint_y = .2
self.auto_dismiss = False
# Label which has a function to properly set the background color
[docs]
class LabelX(Label):
[docs]
def set_bgcolor(self, r, b, g, o):
self.canvas.before.clear()
with self.canvas.before:
Color(r, g, b, o)
self.rect = Rectangle(pos=self.pos, size=self.size)
self.bind(pos=self.update_rect,
size=self.update_rect)
# BoxLayout which has a function to properly set the background color
[docs]
class BoxLayoutX(BoxLayout):
[docs]
def set_bgcolor(self, r, b, g, o):
self.canvas.before.clear()
with self.canvas.before:
Color(r, g, b, o)
self.rect = Rectangle(pos=self.pos, size=self.size)
self.bind(pos=self.update_rect,
size=self.update_rect)
# Scatter class - thids particular implementation of scatter will ensure that the scatter wdget will not
# leave its parent's boundries. Scaling is available through mousewheel action on the scatter
# the scatter will calculate the relative size and position of any widget, given the real screen size and height
[docs]
class ScatterX(Scatter):
outOfRange = False
Init = False
do_rotation = False
videoHeight = 0
videoWidth = 0
videoOffsetX = 0
videoOffsetY = 0
frameWidth = 640
frameHeight = 480
# parameters to be used by opencv
wOffset = (0, 0)
wSize = (0, 0)
[docs]
def apply_transform(self, trans, post_multiply=False, anchor=(0, 0)):
parentX, parentY = self.parent.pos
windowOutRight = False
windowOutLeft = False
windowOutTop = False
windowOutBottom = False
if self.Init:
self.outOfRange = False
transX = trans[12]
transY = trans[13]
x, y = self.pos
# Make sure the Scatter cannot leave the boundries of its parent!!!
if x - parentX + transX < 0:
trans[12] = parentX - x
windowOutLeft = True
if y - parentY + transY < 0:
trans[13] = parentY - y
WindowOutBottom = True
if int(y) + int(self.bbox[1][1]) > int(parentY) + self.parent.height:
trans[13] = int(parentY) + self.parent.height - int(y) - int(self.bbox[1][1])
windowOutTop = True
if int(x) + int(self.bbox[1][0]) > int(parentX) + self.parent.width:
trans[12] = int(parentX) + self.parent.width - int(x) - int(self.bbox[1][0])
windowOutBottom = True
self.Init = True
ret = super(ScatterX, self).apply_transform(trans, post_multiply, anchor)
# calculate video size (for use in opencv)
self.videoWidth = int((self.bbox[1][0] / self.parent.width) * self.frameWidth)
if self.videoWidth > self.frameWidth:
self.videoWidth = self.frameWidth
self.videoHeight = int((self.bbox[1][1] / self.parent.height) * self.frameHeight)
if self.videoHeight > self.frameHeight:
self.videoHeight = self.frameHeight
# calculate video position (for use in opencv)
self.videoOffsetX = int(((self.bbox[0][0] - parentX) / self.parent.width) * self.frameWidth)
if self.videoOffsetX + self.videoWidth > self.frameWidth or windowOutRight:
self.videoOffsetX = self.frameWidth - self.videoWidth
self.videoOffsetY = int(((self.parent.height - self.bbox[0][1] - self.bbox[1][
1] + parentY) / self.parent.height) * self.frameHeight)
if self.videoOffsetY + self.videoHeight > self.frameHeight or windowOutBottom:
self.videoOffsetY = self.frameHeight - self.videoHeight
if self.videoOffsetY < 0 or windowOutTop:
self.videoOffsetY = 0
if self.videoOffsetX < 0 or windowOutLeft:
self.videoOffsetX = 0
return ret
[docs]
def on_touch_move(self, touch): # magic time!!!!
res = super(ScatterX, self).on_touch_move(touch)
if res: # Yay do something!
x, y = self.center
return res
[docs]
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
if touch.is_mouse_scrolling:
self.do_scale = True
if touch.button == 'scrollup':
# if self.bbox[0][0] < self.parent.size[0] or self.bbox[0][1] < self.parent.size[1]:
mat = Matrix().scale(1 / 1.1, 1 / 1.1, 1 / 1.1)
self.apply_transform(mat, anchor=touch.pos)
# self.do_scale = False
# return True
elif touch.button == 'scrolldown':
if self.bbox[1][0] < self.parent.width and self.bbox[1][1] < self.parent.height:
mat = Matrix().scale(1.1, 1.1, 1.1)
self.apply_transform(mat, anchor=touch.pos)
self.do_scale = False
return super().on_touch_up(touch)
[docs]
class TrackPad:
scatter = None
widget = None
[docs]
def getSize(self):
if self.scatter:
return (self.scatter.videoWidth, self.scatter.videoHeight)
else:
return (None, None)
[docs]
def getPosition(self):
if self.scatter:
return (self.scatter.videoOffsetX, self.scatter.videoOffsetY)
else:
return (None, None)
[docs]
def getWidget(self):
trackpadkv = """
BoxLayoutX:
id: mediaplayer_trackpad
size_hint: None, None
size: {wid}, {hgt}
canvas.before:
Color:
rgba: .5,.5,.5,.5
Rectangle:
pos: self.pos
size: self.size
"""
trackpadkv = trackpadkv.format(wid=160, hgt=120)
trackpad = Builder.load_string(trackpadkv)
self.widget = trackpad
return trackpad
[docs]
def createScatter(self, width, height):
sckv = """
ScatterX:
size_hint: None, None
size: {wid}, {hgt}
LabelX:
size: {wid}, {hgt}
canvas.before:
Color:
rgba: .2,.2,.2,1
Rectangle:
pos: self.pos
size: self.size
"""
sckv = sckv.format(wid=width, hgt=height)
self.scatter = Builder.load_string(sckv)
self.widget.clear_widgets()
self.widget.add_widget(self.scatter)
#######################################################################
# VU Meter
#######################################################################
[docs]
class VUMeter:
def __init__(self, width):
self.width = width
self.height = int(width // 299) * 55
self.bars = []
[docs]
def update(self, val):
try:
val = (val) + 18
for i in range(18):
if i < val:
self.bars[i].opacity = 1
else:
self.bars[i].opacity = 0
except:
pass
[docs]
def getWidget(self):
vumeterkv = """
GridLayout:
cols: 36
size_hint: 1, 0.5
# size_hint: None, None
# size: {wid}, {hgt}
"""
vulabelkv = """
Label:
opacity: {opac}
size_hint_x: .95
canvas.before:
Color:
rgba: {col}
Rectangle:
size: self.size
pos: self.pos
"""
import os
vumeterkv = vumeterkv.format(wid=200, hgt=40)
vumeter = Builder.load_string(vumeterkv)
self.widget = vumeter
for i in range(18):
if i < 10:
vulabelkv2 = vulabelkv.format(wid=200, hgt=40, col="0,1,0,1", opac=1)
elif i<15:
vulabelkv2 = vulabelkv.format(wid=200, hgt=40, col="1,1,0,1", opac=1)
else:
vulabelkv2 = vulabelkv.format(wid=200, hgt=40, col="1,0,0,1", opac=1)
vulabel = Builder.load_string(vulabelkv2)
self.widget.add_widget(vulabel)
self.widget.add_widget(Label(size_hint_x=0.4))
self.bars.append(vulabel)
#
return self.widget