Correct. I will post the additional code but omit the function definitions that aren't of interest for readability sake.
Additional information:
2018-10-04 13:16:40 [ERROR ] milkhub.py line:393 unhandledExceptionHandler Stack trace: File "milkhub.py", line 415, in <module>
main()
File "milkhub.py", line 410, in main
application = MilkhubMain(opts.ip, int(opts.instance))
File "milkhub.py", line 186, in __init__
GuiManager.initialise(self)
File "/home/daniel/milkhub-dev/MILKHUBGUI/2.5/src/gui/guicore.py", line 418, in initialise
GuiManager.pixel2d = overlays.PixelNode(app, "g2d")
File "/home/daniel/milkhub-dev/MILKHUBGUI/2.5/src/gui/overlays.py", line 266, in __init__
OverlayContainer.__init__(self, name)
File "/home/daniel/milkhub-dev/MILKHUBGUI/2.5/src/gui/overlays.py", line 170, in __init__
self.name = name
2018-10-05 08:17:55 [INFO ] milkhub.py line:247 destroy Program exited gracefully.
From top of milkhub.py
#!/usr/bin/env python
import re
import os
import sys
import traceback
import logging
import signal
import optparse
import tempfile
from subprocess import Popen, PIPE
import demoSettings as settings
# Setup the logger as early as possible
logger = logging.getLogger("MHGUI")
logger.setLevel(settings.LOGGING_LEVEL)
streamHandler = logging.StreamHandler(sys.stdout)
streamHandler.setLevel(settings.LOGGING_LEVEL)
logFormatter = logging.Formatter(
"%(asctime)s [%(levelname)-7s] %(filename)-22s line:%(lineno)-5d %(funcName)-35s %(message)s", "%Y-%m-%d %H:%M:%S"
)
streamHandler.setFormatter(logFormatter)
logger.addHandler(streamHandler)
from direct.showbase import ShowBase
from panda3d.core import ClockObject, loadPrcFileData, PandaSystem
from demoFunctions import ReadKeys
from gui import GuiManager
from gui.panels import HeaderPanels
from gui.controls import preCacheWebkit # Part of the HtmlPage control
from network import CommsHandlerC
from dataModel import dataModelC
from dialogControlSM import dialogControlSMC
from rotary3d import rotary3dC
from herringbone2d import herringbone2dC
from cameraManager import cameraManagerC
from overallState import overallStateC
from profiler import Profiler
class MilkhubMain(ShowBase.ShowBase):
def __init__(self, ipAddress, instanceId, *args, **kwargs):
"""
Milkhub GUI main application class constructor.
"""
# Set up the exception handler
sys.excepthook = self.unhandledExceptionHandler
# Start with blank objects
self.commsHandler = None
self.dataModel = None
self.rotary3d = None
self.herringbone2d = None
self.keypadSM = None
self.cameraManager = None
self.overallState = None
# Limit FPS
#noinspection PyArgumentList
self.globalClock = ClockObject.getGlobalClock() # we need this elsewhere too
if settings.LIMIT_FPS:
self.globalClock.setMode(ClockObject.MLimited)
self.globalClock.setFrameRate(settings.LIMIT_FPS)
# Detect the current screen resolutions and update the PRC data, so the app won't crash on
# Panda3D 1.7 and up if the screen resolution from demoSettings.py could not be set because
# the monitor could not handle that resolution.
screens = self.get_screen_settings()
logger.info("Screen 1 resolution = %dx%d" % (screens[0][0], screens[0][1]))
if len(screens) > 1:
logger.info("Screen 2 resolution = %dx%d" % (screens[1][0], screens[1][1]))
# Panda3D 1.6 doesn't require you give a screen resolution when switching to full screen on Linux, as it
# didn't support different full screen resolutions (yet) and always used the current resolution for full
# screen. Panda3D 1.7 and 1.8 however, support different resolutions for full screen, which is why we
# must give it a win-size with the resolution we just detected when switching to full screen.
#noinspection PyArgumentList
if not PandaSystem.getVersionString().startswith("1.6") and settings.USE_PRODUCTION_PRC:
loadPrcFileData("", "win-size %d %d" % (screens[0][0], screens[0][1]))
# Xinerama workaround on Intel video cards only. If a window is created with fullscreen on, it seems to
# always use the second screen, so both GUI instances end up on the second screen, which is no good.
# As a workaround, we turn off fullscreen, manually place both windows, and disable the window decoration,
# this will give a fake full screen.
if settings.USE_XINERAMA_HACK and settings.USE_PRODUCTION_PRC:
if "intel" in self.get_video_card().lower():
if instanceId == 1:
#noinspection PyStringFormat
loadPrcFileData("", """
fullscreen 0
win-size %d %d
win-origin %d %d
undecorated 1
""" % screens[0])
elif instanceId == 2:
#noinspection PyStringFormat
loadPrcFileData("", """
fullscreen 0
win-size %d %d
win-origin %d %d
undecorated 1
""" % screens[1])
# store ip address and instance id
self.ipAddress = ipAddress
self.instanceId = instanceId
# call parent class constructor (this will create the main window)
ShowBase.ShowBase.__init__(self, *args, **kwargs)
# create the debug console (press F12 to open)
if settings.DEBUG_CONSOLE_ENABLE:
self.console = DeveloperConsole(self)
if settings.DEBUG_CONSOLE_AUTO_OPEN:
self.console.show()
self.accept("f12-up", self.console.toggle)
# if we want to show logger messages on the console, we create a new StreamHandler to the new sys.stdout
# that is "reprogrammed" by console.py
if settings.DEBUG_CONSOLE_LOGGER:
consoleHandler = logging.StreamHandler(sys.stdout)
consoleHandler.setLevel(settings.LOGGING_LEVEL)
# since we can't fit much on the console, we need a simpler formatter
consoleFormatter = logging.Formatter(
"%(asctime)s \1%(levelname)s\1[%(levelname)s]\2 - %(filename)s(%(lineno)d) - %(funcName)s - %(message)s",
"%H:%M:%S")
consoleHandler.setFormatter(consoleFormatter)
logger.addHandler(consoleHandler)
else:
self.console = None
# if DEBUG_TASKS is enabled, open the task manager debugger window
if settings.DEBUG_TASKS:
self.taskMgr.popupControls()
# initialise the 2D GUI
GuiManager.initialise(self)
# set name of our process (linux only)
self.set_proc_name("milkhub-gui-%d" % instanceId)
# start to webkit2png server
if sys.platform != "win32" and instanceId == 1:
if os.path.exists("webkit2png.py"):
self.webkit2png = Popen(["python", "webkit2png.py"])
else:
#noinspection PyArgumentList
if PandaSystem.getMinorVersion() == 6:
self.webkit2png = Popen(["python2.6", "webkit2png.pyc"])
else:
self.webkit2png = Popen(["python2.7", "webkit2png.pyc"])
preCacheWebkit(self)
else:
self.webkit2png = None
# start the profiler
if settings.SAVE_PROFILER_CSV:
csvPath = os.path.join(tempfile.gettempdir(), "milkhub-gui-%d.csv" % instanceId)
else:
csvPath = None
self.profiler = Profiler(self, csvPath)
# setup our main objects
if settings.USE_DB:
self.db = MilkhubDB()
else:
self.db = None
self.commsHandler = CommsHandlerC(self)
self.dataModel = dataModelC(self)
# pre-generate 2D screens to reduce stutter when switching to each screen the first time
self.headerPanels = HeaderPanels(self)
self.rotary3d = rotary3dC(self)
self.herringbone2d = herringbone2dC(self)
self.keypadSM = dialogControlSMC(self)
self.cameraManager = cameraManagerC(self, initialView="SPLIT_SCREEN_SIDE")
self.overallState = overallStateC(self)
# enter idle state
self.keypadSM.request("IdleState")
# setup keyboard handler for testing
self.keys = ReadKeys(self)
# register our signal handler to receive SIGTERM
signal.signal(signal.SIGTERM, self.signal_handler)
def main():
parser = optparse.OptionParser()
parser.add_option("-i", "--instance", default=1, help="GUI instance 1 or 2 (defaults to 1)")
parser.add_option("-a", "--ip", default="127.0.0.1", help="IP address of Milkhub App (defaults to localhost)")
(opts, args) = parser.parse_args()
application = MilkhubMain(opts.ip, int(opts.instance))
application.run()
if __name__ == "__main__":
main()
This is the class which has the initialise function called above.
import os
import sys
import math
import logging
import tempfile
from shutil import copyfile
from panda3d.core import TextProperties, TextPropertiesManager, Point3
from direct.interval.IntervalGlobal import Sequence, LerpFunc, Wait, Func
from direct.showbase import DirectObject
from direct.gui.DirectGui import DirectFrame
import overlays
from color import Color
from milkhubKeys import keyCodes
import demoSettings as settings
class GuiManager(DirectObject.DirectObject):
# assets URL (passed to Jinja templates as a template variable {{ assets }}
ASSETS_PATH = os.path.abspath(os.path.join(__file__, "..", "..", "assets"))
ASSETS_URL = ASSETS_PATH.replace("\\", "/")
if ASSETS_URL.startswith("/"):
#noinspection PyAugmentAssignment
ASSETS_URL = "file://" + ASSETS_URL
else:
#noinspection PyAugmentAssignment
ASSETS_URL = "file:///" + ASSETS_URL
# gui images
LOGO = "assets/Logos/milkhub_logo.png"
TRUTEST_LOGO = "assets/Logos/trutest_logo.png"
DRAFTING_ICON = "assets/Black Overlays/icons/drafting-screen-icon.png"
CLEANING_ICON = "assets/Black Overlays/icons/cleaning-screen-icon.png"
CLEANING_SUMMARY_ICON = "assets/Black Overlays/icons/cleaning-summary-icon.png"
MILKING_ICON = "assets/Black Overlays/icons/milking-screen-icon.png"
EVENT_ICON = "assets/Black Overlays/icons/event-screen-icon.png"
GENERAL_ICON = "assets/Black Overlays/icons/general-screen-icon.png"
PLANT_ICON = "assets/Black Overlays/icons/plant-screen-icon.png"
PANELBG_LEFT = "assets/Keypad/keypadBKG_L.png"
PANELBG_RIGHT = "assets/Keypad/keypadBKG_R.png"
PANELBG_LARGE_LEFT = "assets/Summary Event/CowRecordBKG-left.png"
PANELBG_LARGE_RIGHT = "assets/Summary Event/CowRecordBKG-right.png"
SIDEBAR_1CAM_LEFT = "assets/Milking Screen/Sidebars/Sidebar_backdrop_L.png"
SIDEBAR_1CAM_RIGHT = "assets/Milking Screen/Sidebars/Sidebar_backdrop_R.png"
SIDEBAR_2CAM_LEFT = "assets/Milking Screen/Sidebars/Sidebar_backdrop_2cam_L.png"
SIDEBAR_2CAM_RIGHT = "assets/Milking Screen/Sidebars/Sidebar_backdrop_2cam_R.png"
CHECK = "assets/Keypad/other/tickbox_tick.png"
NOCHECK = "assets/Keypad/other/tickbox_no_tick.png"
ENTER = "assets/Keypad/keypad_bttns/enter.png"
NUMENTRY_BG = "assets/Keypad/other/cow_bail_num_input_bkg.png"
NUMENTRY_PICKER = "assets/Keypad/other/picker_arrows.png"
SELECTOR = (
"assets/Keypad/other/selector_L.png",
"assets/Keypad/other/selector_C.png",
"assets/Keypad/other/selector_R.png",
)
SCROLL_UP = "assets/Keypad/other/up_arrow.png"
SCROLL_DOWN = "assets/Keypad/other/down_arrow.png"
DRAFT_LEFT = "assets/Milking Screen/Tile/Draft/draft_arrow2_L.png"
DRAFT_RIGHT = "assets/Milking Screen/Tile/Draft/draft_arrow2_R.png"
GRAPH_INSIDE = "assets/Milking Screen/Tile/Content/graph_inside.png"
MILKING_GRAPH = "assets/Milking Screen/Tile/Content/graph_outline.png"
CLEANING_GRAPH = "assets/Cleaning and Plant Screens/loading_bar.png"
SUCCESS_TICK = "assets/Milking Screen/Sidebars/success.png"
ERROR_ICON = "assets/Milking Screen/Sidebars/error.png"
BLANK_GROUP = "assets/Milking Screen/Tile/Groups/blankgroup.png"
PLATE_LEFT = "assets/Cleaning and Plant Screens/info_bkg_L.png"
PLATE_RIGHT = "assets/Cleaning and Plant Screens/info_bkg_R.png"
PLATE_CENTER = "assets/Cleaning and Plant Screens/info_bkg_C.png"
TEMPERATURE_PANEL = "assets/Cleaning and Plant Screens/tempBKGclear.png"
GENERAL_SMALL_BKG = "assets/Summary General/small_BKG.png"
GENERAL_MEDIUM_BKG = "assets/Summary General/medium_BKG.png"
GENERAL_LARGE_BKG = "assets/Summary General/large_BKG.png"
GENERAL_GRAPH_BKG = "assets/Summary General/graph_BKG.png"
ALERT_ICON = "assets/Milking Screen/Tile/Alerts/blank.png"
ALERT_ICON_BG = "assets/Milking Screen/Tile/Alerts/alert-bg.png"
ALERT_ICON_FRAME = "assets/Milking Screen/Tile/Alerts/alert-frame.png"
BLANK_IMAGE = "assets/General/blank.png"
BAIL_TEXTURE = "assets/models/textures/metal_texture.jpg"
POPUP_MESSAGE_BKG = "assets/General/popupMessage.png"
# drafting screen
DRAFT_PLATE = "assets/Drafting Screen/draftplate.png"
HB_MILKING_PLATE = "assets/Milking Screen/hb_plate.png"
# menu icons
MENU_BLANK = "assets/Keypad/menu_keypad_bttns/blank_key.png"
MENU_1 = "assets/Keypad/menu_keypad_bttns/numpad_1.png"
MENU_2 = "assets/Keypad/menu_keypad_bttns/numpad_2.png"
MENU_3 = "assets/Keypad/menu_keypad_bttns/numpad_3.png"
MENU_4 = "assets/Keypad/menu_keypad_bttns/numpad_4.png"
MENU_5 = "assets/Keypad/menu_keypad_bttns/numpad_5.png"
MENU_6 = "assets/Keypad/menu_keypad_bttns/numpad_6.png"
MENU_7 = "assets/Keypad/menu_keypad_bttns/numpad_7.png"
MENU_8 = "assets/Keypad/menu_keypad_bttns/numpad_8.png"
MENU_9 = "assets/Keypad/menu_keypad_bttns/numpad_9.png"
MENU_0 = "assets/Keypad/menu_keypad_bttns/numpad_0.png"
MENU_BAIL = "assets/Keypad/menu_keypad_bttns/bail.png"
MENU_EARTAG = "assets/Keypad/menu_keypad_bttns/eartag.png"
MENU_UP = "assets/Keypad/menu_keypad_bttns/up_arrow.png"
MENU_DOWN = "assets/Keypad/menu_keypad_bttns/down_arrow.png"
MENU_OTHER = "assets/Keypad/menu_keypad_bttns/other.png"
MENU_MAST = "assets/Keypad/menu_keypad_bttns/mast.png"
MENU_FOOT = "assets/Keypad/menu_keypad_bttns/foot.png"
MENU_TREAT = "assets/Keypad/menu_keypad_bttns/treat.png"
MENU_AB = "assets/Keypad/menu_keypad_bttns/ab.png"
MENU_PD = "assets/Keypad/menu_keypad_bttns/pd.png"
MENU_SHIFT = "assets/Keypad/menu_keypad_bttns/shift.png"
MENU_SYSTEM = "assets/Keypad/menu_keypad_bttns/system.png"
MENU_CLEAR = "assets/Keypad/menu_keypad_bttns/clear.png"
MENU_ENTER = "assets/Keypad/menu_keypad_bttns/enter.png"
PULLCORD = "assets/Keypad/pullcord.png"
# keypad
KEYPAD = "assets/Keypad/keypad_blank.png"
KEYPAD_1 = "assets/Keypad/keypad_bttns/numpad_1.png"
KEYPAD_2 = "assets/Keypad/keypad_bttns/numpad_2.png"
KEYPAD_3 = "assets/Keypad/keypad_bttns/numpad_3.png"
KEYPAD_4 = "assets/Keypad/keypad_bttns/numpad_4.png"
KEYPAD_5 = "assets/Keypad/keypad_bttns/numpad_5.png"
KEYPAD_6 = "assets/Keypad/keypad_bttns/numpad_6.png"
KEYPAD_7 = "assets/Keypad/keypad_bttns/numpad_7.png"
KEYPAD_8 = "assets/Keypad/keypad_bttns/numpad_8.png"
KEYPAD_9 = "assets/Keypad/keypad_bttns/numpad_9.png"
KEYPAD_0 = "assets/Keypad/keypad_bttns/numpad_0.png"
KEYPAD_BAIL = "assets/Keypad/keypad_bttns/bail.png"
KEYPAD_EARTAG = "assets/Keypad/keypad_bttns/eartag.png"
KEYPAD_UP = "assets/Keypad/keypad_bttns/up_arrow.png"
KEYPAD_DOWN = "assets/Keypad/keypad_bttns/down_arrow.png"
KEYPAD_OTHER = "assets/Keypad/keypad_bttns/other.png"
KEYPAD_MAST = "assets/Keypad/keypad_bttns/mast.png"
KEYPAD_FOOT = "assets/Keypad/keypad_bttns/foot.png"
KEYPAD_TREAT = "assets/Keypad/keypad_bttns/treat.png"
KEYPAD_AB = "assets/Keypad/keypad_bttns/ab.png"
KEYPAD_PD = "assets/Keypad/keypad_bttns/pd.png"
KEYPAD_SHIFT = "assets/Keypad/keypad_bttns/shift.png"
KEYPAD_SYSTEM = "assets/Keypad/keypad_bttns/system.png"
KEYPAD_CLEAR = "assets/Keypad/keypad_bttns/clear.png"
KEYPAD_ENTER = "assets/Keypad/keypad_bttns/enter.png"
# cleaning screen
CLEANING_LEGEND = "assets/Cleaning and Plant Screens/cleaningScreenlegend.png"
CLEANING_LEGEND_BAR = "assets/Cleaning and Plant Screens/cleaningScreenlegendColourBar.png"
CLEANING_LEFT_BACKGROUND = "assets/Cleaning and Plant Screens/cleaningScreenLeftBKG.png"
CLEANING_LEFT_AVGTEMP = "assets/Cleaning and Plant Screens/cleaningScreenBlackBoxText.png"
CLEANING_DEGREES = "assets/Cleaning and Plant Screens/cleaningScreenDegrees.png"
CLEANING_FILLER_L = "assets/Cleaning and Plant Screens/cleaningFiller_L.png"
CLEANING_FILLER_R = "assets/Cleaning and Plant Screens/cleaningFiller_R.png"
# animations
KEYPRESS_ANIM = ["assets/Keypad/key_presses/keypad_%s.png" % str(frame).zfill(5) for frame in range(15)]
CLEANING_ANIM = ["assets/Cleaning and Plant Screens/loader%d.png" % frame for frame in range(9)]
# key to keypad icon lookup table
keypadIcon = {
keyCodes.KEY_ONE: KEYPAD_1,
keyCodes.KEY_TWO: KEYPAD_2,
keyCodes.KEY_THREE: KEYPAD_3,
keyCodes.KEY_FOUR: KEYPAD_4,
keyCodes.KEY_FIVE: KEYPAD_5,
keyCodes.KEY_SIX: KEYPAD_6,
keyCodes.KEY_SEVEN: KEYPAD_7,
keyCodes.KEY_EIGHT: KEYPAD_8,
keyCodes.KEY_NINE: KEYPAD_9,
keyCodes.KEY_ZERO: KEYPAD_0,
keyCodes.KEY_BAIL: KEYPAD_BAIL,
keyCodes.KEY_TAG: KEYPAD_EARTAG,
keyCodes.KEY_UP: KEYPAD_UP,
keyCodes.KEY_DOWN: KEYPAD_DOWN,
keyCodes.KEY_OTHER: KEYPAD_OTHER,
keyCodes.KEY_MAST: KEYPAD_MAST,
keyCodes.KEY_FOOT: KEYPAD_FOOT,
keyCodes.KEY_TREAT: KEYPAD_TREAT,
keyCodes.KEY_AB: KEYPAD_AB,
keyCodes.KEY_PD: KEYPAD_PD,
keyCodes.KEY_SHIFT: KEYPAD_SHIFT,
keyCodes.KEY_SYSTEM: KEYPAD_SYSTEM,
keyCodes.KEY_CLEAR: KEYPAD_CLEAR,
keyCodes.KEY_ENTER: KEYPAD_ENTER
}
# key to menu icon lookup table
menuIcon = {
None: MENU_BLANK,
keyCodes.KEY_ONE: MENU_1,
keyCodes.KEY_TWO: MENU_2,
keyCodes.KEY_THREE: MENU_3,
keyCodes.KEY_FOUR: MENU_4,
keyCodes.KEY_FIVE: MENU_5,
keyCodes.KEY_SIX: MENU_6,
keyCodes.KEY_SEVEN: MENU_7,
keyCodes.KEY_EIGHT: MENU_8,
keyCodes.KEY_NINE: MENU_9,
keyCodes.KEY_ZERO: MENU_0,
keyCodes.KEY_BAIL: MENU_BAIL,
keyCodes.KEY_TAG: MENU_EARTAG,
keyCodes.KEY_UP: MENU_UP,
keyCodes.KEY_DOWN: MENU_DOWN,
keyCodes.KEY_OTHER: MENU_OTHER,
keyCodes.KEY_MAST: MENU_MAST,
keyCodes.KEY_FOOT: MENU_FOOT,
keyCodes.KEY_TREAT: MENU_TREAT,
keyCodes.KEY_AB: MENU_AB,
keyCodes.KEY_PD: MENU_PD,
keyCodes.KEY_SHIFT: MENU_SHIFT,
keyCodes.KEY_SYSTEM: MENU_SYSTEM,
keyCodes.KEY_CLEAR: MENU_CLEAR,
keyCodes.KEY_ENTER: MENU_ENTER
}
# These class variables get set when initialise() is called
app = None
dummy_overlay = None
pixel2d = None
width = 0
height = 0
screenWidth = 0
screenHeight = 0
oldScreenWidth = None
oldScreenHeight = None
resolutionChanged = False
flashPopup = None
flashPopupLerp = None
bulletinMessagePanel = None
bulletinMessageLerp = None
# Legacy constants, no longer used by the 2D gui, but still in 3D, these get set after initialise() is called
FONT_BOLD_HIRES = None
FONT_BOLD_HIRES_OUTLINE = None
@staticmethod
def initialise(app, width=1920, height=1080):
"""
Initialises the :data:`gui.GuiManager` class, this is called once when the application first starts,
and should be called before we do any GUI operations.
The ``width`` and ``height`` parameters are not the actual screen resolution the application is running at,
but rather the desired resolution for the GUI controls, the controls will scale to the actual real screen
resolution, but coordinates are still in the "desired" screen resolution defined by ``width`` and ``height``.
For the Milkhub GUI the desired resolution is always 1920x1080 and shouldn't really be changed, or things
will generally look all wrong, or zoomed in.
"""
GuiManager.app = app
GuiManager.setDesiredResolution(width, height)
GuiManager.resolutionChanged = False # not wanted on first run, only when window resizes
# initialise pixel2d
GuiManager.pixel2d = overlays.PixelNode(app, "g2d")
GuiManager.pixel2d.controls = []
GuiManager.pixel2d.getContainer = lambda: GuiManager.pixel2d
# the dummy overlay is used to "hide" elements, by reparenting them to the dummy overlay
GuiManager.dummy_overlay = overlays.Overlay()
# Store the current and old screen width and height. This is used by Font.resizeFont() to bypass a memory leak
# in Panda3D 1.6. We store the old window width and height, so we only resize the fonts if the window is
# actually resized, but not when we are in full screen. The only downside is, that, if you resize the window,
# it will still leak memory, but if you run it in full screen as it is in the farm, or don't resize the window
# too often it's fine.
GuiManager.screenWidth = float(GuiManager.app.win.getXSize())
GuiManager.screenHeight = float(GuiManager.app.win.getYSize())
GuiManager.oldScreenWidth = GuiManager.screenWidth
GuiManager.oldScreenHeight = GuiManager.screenHeight
# adds some text properties so you can make part of a text label Milkhub gold
GuiManager.setupTextProperties()
# initialise font manager
Font.initialise()
# create some font constants to be used in the 3D world only, compatible with the old font system
# that we originally used in the 2D GUI until we changed all that.
GuiManager.FONT_BOLD_HIRES = Font.loadFont(Font.BOLD, 150)
GuiManager.FONT_BOLD_HIRES_OUTLINE = Font.loadFont(Font.BOLD, 150, outline=(Color.BLACK, 0.5, 0.4))
# initialise our aspectRatioChanged handler
GuiManager.app.accept("aspectRatioChanged", GuiManager.aspectRatioChanged)
# create popup message boxes
GuiManager.createFlashPopup()
GuiManager.playingFlashMessage = False
GuiManager.flashMessageQueue = []
# create a large black frame to blackout the whole screen as a hack to "turn the display off"
GuiManager.blackOutFrame = DirectFrame(frameColor=Color.BLACK, frameSize=(-2, 2, -1, 1), pos=(0, 0, 0))
GuiManager.blackOutFrame.hide()
GuiManager.screenBlackedOut = False
#
from panels.basic import BasicPanel
from controls.labels import Label
GuiManager.bulletinMessagePanel = BasicPanel(
GuiManager.pixel2d, # parent node
size=(1920, 55),
pos=(0, 1080),
color=Color.WHITE,
zIndex=200
)
GuiManager.bulletinMessagePanel.caption = Label(
GuiManager.bulletinMessagePanel, # parent node
pos=(960, 5),
caption="",
fontSize=Font.SIZE_MEDIUM,
color=Color.BLACK,
align=Position.CENTER
)