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
[docs] class ValidatePopup(Popup): pass
[docs] class SideBarMenu(FloatLayout): pass
# The Rewards Screen
[docs] class RewardsScreen(Screen): pass
# 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
[docs] class ShowNTellScreen(Screen): pass
# class for The Tools screen
[docs] class ToolsScreen(Screen): pass
####################################################################### # 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())
[docs] def sortByWeight(self, item): return item[1]
[docs] def closeSearch(self): pass
# 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 activateTestMode(self, *args): OSUtility.Restart(True)
[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 showSplashScreen(self): TkinterAPI.showAboutScreen()
[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)
####################################################################### # 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
[docs] def changeName(self): id = InputTextDialog() id.show(callback=self.changeButtonName)
[docs] def changeButtonName(self, text): self.activeButton.text = text
################################################################## """ @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")
[docs] def emptyFunction(self): pass
###################################################################################################################
[docs] class ItemIcon(Image): def __init__(self, **kwargs): super().__init__(**kwargs)
[docs] def makeIcon(self): self.size_hint = (None, None) self.size = ("92sp", "92sp")
################################################################################################################### ## 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()
[docs] def emptyDef(self, *args): self.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 autoClose(self, *args): Clock.schedule_once(self.closeNotification, 2)
[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)
[docs] def onOpen(self, *args, **kwargs): self.InfoPopupObject.dismiss() self.callbackOpen()
[docs] def uninstallPlugin(self, *args, **kwargs): self.InfoPopupObject.dismiss()
# 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()
[docs] def validate(self, *args): print(args)
####################### # 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
[docs] def show(self, text): self.createLoader(text) self.open()
[docs] def close(self): self.dismiss()
# 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)
[docs] def update_rect(self, *args): self.rect.pos = self.pos self.rect.size = self.size
# 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)
[docs] def update_rect(self, *args): self.rect.pos = self.pos self.rect.size = self.size
# 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