CampBuddy/Camp.Buddy v2.2.1/Camp_Buddy-2.2.1-pc/renpy/display/screen.py
2025-03-03 23:00:33 +01:00

1394 lines
35 KiB
Python

# Copyright 2004-2019 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import print_function
import renpy.display
import time
import collections
import datetime
# Profiling ####################################################################
profile_log = renpy.log.open("profile_screen", developer=True, append=False, flush=False)
# A map from screen name to ScreenProfile object.
profile = { }
class ScreenProfile(renpy.object.Object):
"""
:doc: profile_screen
:name: renpy.profile_screen
"""
def __init__(self, name, predict=False, show=False, update=False, request=False, time=False, debug=False, const=False):
"""
Requests screen profiling for the screen named `name`, which
must be a string.
Apart from `name`, all arguments must be supplied as keyword
arguments. This function takes three groups of arguments.
The first group of arguments determines when profiling occurs.
`predict`
If true, profiling occurs when the screen is being predicted.
`show`
If true, profiling occurs when the screen is first shown.
`update`
If true, profiling occurs when the screen is updated.
`request`
If true, profiling occurs when requested by pressing F8.
The second group of arguments controls what profiling output is
produced when profiling occurs.
`time`
If true, Ren'Py will log the amount of time it takes to evaluate
the screen.
`debug`
If true, Ren'Py will log information as to how screens are
evaluated, including:
* Which displayables Ren'Py considers constant.
* Which arguments, if any, needed to be evaluated.
* Which displayables were reused.
Producing and saving this debug information takes a noticeable
amount of time, and so the `time` output should not be considered
reliable if `debug` is set.
The last group of arguments controls what output is produced once
per Ren'Py run.
`const`
Displays the variables in the screen that are marked as const and
not-const.
All profiling output will be logged to profile_screen.txt in the game
directory.
"""
self.predict = predict
self.show = show
self.update = update
self.request = request
self.time = time
self.debug = debug
self.const = const
if name is not None:
if isinstance(name, basestring):
name = tuple(name.split())
profile[name] = self
def get_profile(name):
"""
Returns the profile object for the screen with `name`, or a default
profile object if none exists.
`name`
A string or tuple.
"""
if isinstance(name, basestring):
name = tuple(name.split())
if name in profile:
return profile[name]
else:
return ScreenProfile(None)
# Cache ########################################################################
# A map from screen name to a list of ScreenCache objects. We ensure the cache
# does not exceed config.screen_cache_size for each screen.
predict_cache = collections.defaultdict(list)
class ScreenCache(object):
"""
Represents an entry in the screen cache. Upon creation, puts itself into
the screen cache.
"""
def __init__(self, screen, args, kwargs, cache):
if screen.ast is None:
return
self.args = args
self.kwargs = kwargs
self.cache = cache
pc = predict_cache[screen]
pc.append(self)
if len(pc) > renpy.config.screen_cache_size:
pc.pop(0)
cache_put = ScreenCache
def cache_get(screen, args, kwargs):
"""
Returns the cache to use when `screen` is accessed with `args` and
`kwargs`.
"""
if screen.ast is None:
return { }
pc = predict_cache[screen]
if not pc:
return { }
for sc in pc:
# Reuse w/ same arguments.
if sc.args == args and sc.args == kwargs:
pc.remove(sc)
break
else:
# Reuse the oldest.
sc = pc.pop(0)
return sc.cache
# Screens #####################################################################
class Screen(renpy.object.Object):
"""
A screen is a collection of widgets that are displayed together.
This class stores information about the screen.
"""
sensitive = "True"
def __init__(self,
name,
function,
modal="False",
zorder="0",
tag=None,
predict=None,
variant=None,
parameters=False,
location=None,
layer="screens",
sensitive="True"):
# The name of this screen.
if isinstance(name, basestring):
name = tuple(name.split())
self.name = name
if (variant is None) or isinstance(variant, basestring):
variant = [ variant ]
for v in variant:
screens[name[0], v] = self
screens_by_name[name[0]][v] = self
# A function that can be called to display the screen.
self.function = function
# If this is a SL2 screen, the SLScreen node at the root of this
# screen.
if isinstance(function, renpy.sl2.slast.SLScreen): # @UndefinedVariable
self.ast = function
else:
self.ast = None
# Expression: Are we modal? (A modal screen ignores screens under it.)
self.modal = modal
# Expression: Our zorder.
self.zorder = zorder
# The tag associated with the screen.
self.tag = tag or name[0]
# Can this screen be predicted?
if predict is None:
predict = renpy.config.predict_screens
self.predict = predict
# True if this screen takes parameters via _args and _kwargs.
self.parameters = parameters
# The location (filename, linenumber) of this screen.
self.location = location
# The layer the screen will be shown on.
self.layer = layer
# Is this screen sensitive? An expression.
self.sensitive = sensitive
global prepared
global analyzed
prepared = False
analyzed = False
# Phases we can be in.
PREDICT = 0 # Predicting the screen before it is shown.
SHOW = 1 # Showing the screen for the first time.
UPDATE = 2 # Showing the screen for the second and later times.
HIDE = 3 # After the screen has been hid with "hide screen" (or the end of call screen).
OLD = 4 # A copy of the screen in the old side of a transition.
phase_name = [
"PREDICT",
"SHOW",
"UPDATE",
"HIDE",
"OLD",
]
class ScreenDisplayable(renpy.display.layout.Container):
"""
A screen is a collection of widgets that are displayed together. This
class is responsible for managing the display of a screen.
"""
nosave = [
'screen',
'child',
'children',
'transforms',
'widgets',
'old_widgets',
'hidden_widgets',
'old_transforms',
'cache',
'miss_cache',
'profile',
'phase',
'use_cache' ]
restarting = False
hiding = False
transient = False
def after_setstate(self):
self.screen = get_screen_variant(self.screen_name[0])
self.child = None
self.children = [ ]
self.transforms = { }
self.widgets = { }
self.old_widgets = None
self.old_transforms = None
self.hidden_widgets = { }
self.cache = { }
self.phase = UPDATE
self.use_cache = { }
self.miss_cache = { }
self.profile = profile.get(self.screen_name, None)
def __init__(self, screen, tag, layer, widget_properties={}, scope={}, transient=False, **properties):
super(ScreenDisplayable, self).__init__(**properties)
# Stash the properties, so we can re-create the screen.
self.properties = properties
# The screen, and it's name. (The name is used to look up the
# screen on save.)
self.screen = screen
self.screen_name = screen.name
self._location = self.screen.location
# The profile object that determines when we profile.
self.profile = profile.get(self.screen_name, None)
# The tag and layer screen was displayed with.
self.tag = tag
self.layer = layer
# The scope associated with this statement. This is passed in
# as keyword arguments to the displayable.
self.scope = renpy.python.RevertableDict(scope)
# The child associated with this screen.
self.child = None
# Widget properties given to this screen the last time it was
# shown.
self.widget_properties = widget_properties
# A map from name to the widget with that name.
self.widgets = { }
# The persistent cache.
self.cache = { }
if tag and layer:
old_screen = get_screen(tag, layer)
else:
old_screen = None
# A map from name to the transform with that name. (This is
# taken from the old version of the screen, if it exists.
if old_screen is not None:
self.transforms = old_screen.transforms
else:
self.transforms = { }
# A map from a (screen name, id) pair to cache. This is for use
# statements with the id parameter.
if old_screen is not None:
self.use_cache = old_screen.use_cache
else:
self.use_cache = { }
# A version of the cache that's used when we have a screen that is
# being displayed with the same tag with a cached copy of the screen
# we want to display.
self.miss_cache = { }
# What widgets and transforms were the last time this screen was
# updated. Used to communicate with the ui module, and only
# valid during an update - not used at other times.
self.old_widgets = None
self.old_transforms = None
# Should we transfer data from the old_screen? This becomes
# true once this screen finishes updating for the first time,
# and also while we're using something.
self.old_transfers = (old_screen and old_screen.screen_name == self.screen_name)
# The current transform event, and the last transform event to
# be processed.
self.current_transform_event = None
# A dict-set of widgets (by id) that have been hidden from us.
self.hidden_widgets = { }
# Are we restarting or hiding?
self.restarting = False
self.hiding = False
# Is this a transient screen?
self.transient = transient
# Modal and zorder.
self.modal = renpy.python.py_eval(self.screen.modal, locals=self.scope)
self.zorder = renpy.python.py_eval(self.screen.zorder, locals=self.scope)
# The lifecycle phase we are in - one of PREDICT, SHOW, UPDATE, or HIDE.
self.phase = PREDICT
def __unicode__(self):
return "Screen {}".format(" ".join(self.screen_name))
def visit(self):
return [ self.child ]
def visit_all(self, callback, seen=None):
callback(self)
try:
push_current_screen(self)
self.child.visit_all(callback, seen=None)
finally:
pop_current_screen()
def per_interact(self):
renpy.display.render.redraw(self, 0)
self.update()
def set_transform_event(self, event):
super(ScreenDisplayable, self).set_transform_event(event)
self.current_transform_event = event
def find_focusable(self, callback, focus_name):
hiding = (self.phase == OLD) or (self.phase == HIDE)
try:
push_current_screen(self)
if self.child and not hiding:
self.child.find_focusable(callback, focus_name)
finally:
pop_current_screen()
def copy(self):
rv = ScreenDisplayable(self.screen, self.tag, self.layer, self.widget_properties, self.scope, **self.properties)
rv.transforms = self.transforms.copy()
rv.widgets = self.widgets.copy()
rv.old_transfers = True
rv.child = self.child
return rv
def _handles_event(self, event):
if self.child is None:
if self.transient:
return False
self.update()
return self.child._handles_event(event)
def _hide(self, st, at, kind):
if self.phase == HIDE:
hid = self
else:
if (self.child is not None) and (not self.child._handles_event(kind)):
return None
updated_screens.discard(self)
self.update()
if self.screen is None:
return None
if self.child is None:
return None
if not self.child._handles_event(kind):
return None
if self.screen.ast is not None:
self.screen.ast.copy_on_change(self.cache.get(0, {}))
hid = self.copy()
for i in self.child.children:
i.set_transform_event(kind)
hid.phase = HIDE
rv = None
old_child = hid.child
if not isinstance(old_child, renpy.display.layout.MultiBox):
return None
renpy.ui.detached()
hid.child = renpy.ui.default_fixed(focus="_screen_" + "_".join(self.screen_name))
hid.children = [ hid.child ]
renpy.ui.close()
for d in old_child.children:
c = d._hide(st, at, kind)
if c is not None:
renpy.display.render.redraw(c, 0)
hid.child.add(c)
rv = hid
if hid is not None:
renpy.display.render.redraw(hid, 0)
return rv
def _in_current_store(self):
if self.screen is None:
return self
if self.child is None:
return self
if not renpy.config.transition_screens:
return self
if self.screen.ast is not None:
self.screen.ast.copy_on_change(self.cache.get(0, {}))
rv = self.copy()
rv.phase = OLD
rv.child = self.child._in_current_store()
return rv
def update(self):
if self in updated_screens:
return
updated_screens.add(self)
if self.screen is None:
self.child = renpy.display.layout.Null()
return { }
# Do not update if restarting or hiding.
if self.restarting or (self.phase == HIDE) or (self.phase == OLD):
if not self.child:
self.child = renpy.display.layout.Null()
return self.widgets
profile = False
debug = False
if self.profile:
if self.phase == UPDATE and self.profile.update:
profile = True
elif self.phase == SHOW and self.profile.show:
profile = True
elif self.phase == PREDICT and self.profile.predict:
profile = True
if renpy.display.interface.profile_once and self.profile.request:
profile = True
if profile:
profile_log.write("%s %s %s",
phase_name[self.phase],
" ".join(self.screen_name),
datetime.datetime.now().strftime("%H:%M:%S.%f"))
start = time.time()
if self.profile.debug:
debug = True
# Cycle widgets and transforms.
self.old_widgets = self.widgets
self.old_transforms = self.transforms
self.widgets = { }
self.transforms = { }
push_current_screen(self)
old_ui_screen = renpy.ui.screen
renpy.ui.screen = self
# The name of the root screen of this screen.
NAME = 0
old_cache = self.cache.get(NAME, None)
# Evaluate the screen.
try:
renpy.ui.detached()
self.child = renpy.ui.default_fixed(focus="_screen_" + "_".join(self.screen_name))
self.children = [ self.child ]
self.scope["_scope"] = self.scope
self.scope["_name"] = NAME
self.scope["_debug"] = debug
self.screen.function(**self.scope)
renpy.ui.close()
finally:
del self.scope["_scope"]
renpy.ui.screen = old_ui_screen
pop_current_screen()
# Finish up.
self.old_widgets = None
self.old_transforms = None
self.old_transfers = True
if self.miss_cache:
self.miss_cache.clear()
# Deal with the case where the screen version changes.
if (self.cache.get(NAME, None) is not old_cache) and (self.current_transform_event is None) and (self.phase == UPDATE):
self.current_transform_event = "update"
if self.current_transform_event:
for i in self.child.children:
i.set_transform_event(self.current_transform_event)
self.current_transform_event = None
if profile:
end = time.time()
if self.profile.time:
profile_log.write("* %.2f ms", 1000 * (end - start))
if self.profile.debug:
profile_log.write("\n")
return self.widgets
def render(self, w, h, st, at):
if not self.child:
self.update()
if self.phase == SHOW:
self.phase = UPDATE
try:
push_current_screen(self)
child = renpy.display.render.render(self.child, w, h, st, at)
finally:
pop_current_screen()
rv = renpy.display.render.Render(w, h)
rv.focus_screen = self
hiding = (self.phase == OLD) or (self.phase == HIDE)
if self.screen is None:
sensitive = False
else:
sensitive = renpy.python.py_eval(self.screen.sensitive, locals=self.scope)
rv.blit(child, (0, 0), focus=sensitive and not hiding, main=not hiding)
rv.modal = self.modal and not hiding
return rv
def get_placement(self):
if not self.child:
self.update()
return self.child.get_placement()
def event(self, ev, x, y, st):
if (self.phase == OLD) or (self.phase == HIDE):
return
if not self.screen:
return None
if not renpy.python.py_eval(self.screen.sensitive, locals=self.scope):
ev = renpy.display.interface.time_event
try:
push_current_screen(self)
rv = self.child.event(ev, x, y, st)
finally:
pop_current_screen()
if rv is not None:
return rv
if self.modal:
raise renpy.display.layout.IgnoreLayers()
def get_phase_name(self):
return phase_name[self.phase]
# The name of the screen that is currently being displayed, or
# None if no screen is being currently displayed.
_current_screen = None
# The stack of old current screens.
current_screen_stack = [ ]
def push_current_screen(screen):
global _current_screen
current_screen_stack.append(_current_screen)
_current_screen = screen
def pop_current_screen():
global _current_screen
_current_screen = current_screen_stack.pop()
# A map from (screen_name, variant) tuples to screen.
screens = { }
# A map from screen name to map from variant to screen.
screens_by_name = collections.defaultdict(dict)
# The screens that were updated during the current interaction.
updated_screens = set()
def get_screen_variant(name, candidates=None):
"""
Get a variant screen object for `name`.
`candidates`
A list of candidate variants.
"""
if candidates is None:
candidates = renpy.config.variants
for i in candidates:
rv = screens.get((name, i), None)
if rv is not None:
return rv
return None
def get_all_screen_variants(name):
"""
Gets all variants of the screen with `name`.
Returns a list of (`variant`, `screen`) tuples, in no particular
order.
"""
rv = [ ]
for k, v in screens.iteritems():
if k[0] == name:
rv.append((k[1], v))
return rv
# Have all screens been analyzed?
analyzed = False
# Have the screens been prepared?
prepared = False
# Caches for sort_screens.
sorted_screens = [ ]
screens_at_sort = { }
# The list of screens that participate in a use cycle.
use_cycle = [ ]
def sort_screens():
"""
Produces a list of SL2 screens in topologically sorted order.
"""
global use_cycle
global sorted_screens
global screens_at_sort
if screens_at_sort == screens:
return sorted_screens
# For each screen, the set of screens it uses.
depends = collections.defaultdict(set)
# For each screen, the set of screens that use it.
reverse = collections.defaultdict(set)
names = { i[0] for i in screens }
for k, v in screens.items():
name = k[0]
# Ensure name exists.
depends[name]
if not v.ast:
continue
def callback(uses):
if uses not in names:
return
depends[name].add(uses)
reverse[uses].add(name)
v.ast.used_screens(callback)
rv = [ ]
workset = { k for k, v in depends.items() if not len(v) }
while workset:
name = workset.pop()
rv.append(name)
for i in reverse[name]:
d = depends[i]
d.remove(name)
if not d:
workset.add(i)
del reverse[name]
# Store the use cycle for later reporting.
use_cycle = reverse.keys()
use_cycle.sort()
sorted_screens = rv
screens_at_sort = dict(screens)
return rv
def sorted_variants():
"""
Produces a list of screen variants in topological order.
"""
rv = [ ]
for name in sort_screens():
rv.extend(screens_by_name[name].values())
return rv
def analyze_screens():
"""
Analyzes all screens.
"""
global analyzed
if analyzed:
return
for s in sorted_variants():
if s.ast is None:
continue
s.ast.analyze_screen()
analyzed = True
def prepare_screens():
"""
Prepares all screens for use.
"""
global prepared
if prepared:
return
predict_cache.clear()
old_predicting = renpy.display.predict.predicting
renpy.display.predict.predicting = True
try:
if not analyzed:
analyze_screens()
for s in sorted_variants():
if s.ast is None:
continue
s.ast.unprepare_screen()
s.ast.prepare_screen()
prepared = True
finally:
renpy.display.predict.predicting = old_predicting
if renpy.config.developer and use_cycle:
raise Exception("The following screens use each other in a loop: " + ", ".join(use_cycle) +". This is not allowed.")
def define_screen(*args, **kwargs):
"""
:doc: screens
:args: (name, function, modal="False", zorder="0", tag=None, variant=None)
Defines a screen with `name`, which should be a string.
`function`
The function that is called to display the screen. The
function is called with the screen scope as keyword
arguments. It should ignore additional keyword arguments.
The function should call the ui functions to add things to the
screen.
`modal`
A string that, when evaluated, determines of the created
screen should be modal. A modal screen prevents screens
underneath it from receiving input events.
`zorder`
A string that, when evaluated, should be an integer. The integer
controls the order in which screens are displayed. A screen
with a greater zorder number is displayed above screens with a
lesser zorder number.
`tag`
The tag associated with this screen. When the screen is shown,
it replaces any other screen with the same tag. The tag
defaults to the name of the screen.
`predict`
If true, this screen can be loaded for image prediction. If false,
it can't. Defaults to true.
`variant`
String. Gives the variant of the screen to use.
"""
Screen(*args, **kwargs)
def get_screen_layer(name):
"""
Returns the layer that the screen with `name` is part of.
"""
if not isinstance(name, basestring):
name = name[0]
screen = get_screen_variant(name)
if screen is None:
return "screens"
else:
return screen.layer
def get_screen(name, layer=None):
"""
:doc: screens
Returns the ScreenDisplayable with the given `name` on layer. `name`
is first interpreted as a tag name, and then a screen name. If the
screen is not showing, returns None.
This can also take a list of names, in which case the first screen
that is showing is returned.
This function can be used to check if a screen is showing::
if renpy.get_screen("say"):
text "The say screen is showing."
else:
text "The say screen is hidden."
"""
if layer is None:
layer = get_screen_layer(name)
if isinstance(name, basestring):
name = (name, )
sl = renpy.exports.scene_lists()
for tag in name:
sd = sl.get_displayable_by_tag(layer, tag)
if sd is not None:
return sd
for tag in name:
sd = sl.get_displayable_by_name(layer, (tag, ))
if sd is not None:
return sd
return None
def has_screen(name):
"""
Returns true if a screen with the given name exists.
"""
if not isinstance(name, tuple):
name = tuple(name.split())
if not name:
return False
if get_screen_variant(name[0]):
return True
else:
return False
def show_screen(_screen_name, *_args, **kwargs):
"""
:doc: screens
The programmatic equivalent of the show screen statement.
Shows the named screen. This takes the following keyword arguments:
`_screen_name`
The name of the screen to show.
`_layer`
The layer to show the screen on.
`_zorder`
The zorder to show the screen on. If not specified, defaults to
the zorder associated with the screen. It that's not specified,
it is 0 by default.
`_tag`
The tag to show the screen with. If not specified, defaults to
the tag associated with the screen. It that's not specified,
defaults to the name of the screen.
`_widget_properties`
A map from the id of a widget to a property name -> property
value map. When a widget with that id is shown by the screen,
the specified properties are added to it.
`_transient`
If true, the screen will be automatically hidden at the end of
the current interaction.
Non-keyword arguments, and keyword arguments that do not begin with
an underscore, are passed to the screen.
"""
_layer = kwargs.pop("_layer", None)
_tag = kwargs.pop("_tag", None)
_widget_properties = kwargs.pop("_widget_properties", {})
_transient = kwargs.pop("_transient", False)
_zorder = kwargs.pop("_zorder", None)
name = _screen_name
if not isinstance(name, tuple):
name = tuple(name.split())
screen = get_screen_variant(name[0])
if screen is None:
raise Exception("Screen %s is not known.\n" % (name[0],))
if _layer is None:
_layer = get_screen_layer(name)
if _tag is None:
_tag = screen.tag
scope = { }
if screen.parameters:
scope["_kwargs" ] = kwargs
scope["_args"] = _args
else:
scope.update(kwargs)
d = ScreenDisplayable(screen, _tag, _layer, _widget_properties, scope, transient=_transient)
if _zorder is None:
_zorder = d.zorder
old_d = get_screen(_tag, _layer)
if old_d and old_d.cache:
d.cache = old_d.cache
d.miss_cache = cache_get(screen, _args, kwargs)
d.phase = UPDATE
else:
d.cache = cache_get(screen, _args, kwargs)
d.phase = SHOW
sls = renpy.display.core.scene_lists()
sls.add(_layer, d, _tag, zorder=_zorder, transient=_transient, keep_st=True, name=name)
def predict_screen(_screen_name, *_args, **kwargs):
"""
Predicts the displayables that make up the given screen.
`_screen_name`
The name of the screen to show.
`_widget_properties`
A map from the id of a widget to a property name -> property
value map. When a widget with that id is shown by the screen,
the specified properties are added to it.
Keyword arguments not beginning with underscore (_) are used to
initialize the screen's scope.
"""
_layer = kwargs.pop("_layer", None)
_tag = kwargs.pop("_tag", None)
_widget_properties = kwargs.pop("_widget_properties", {})
_transient = kwargs.pop("_transient", False)
name = _screen_name
if renpy.config.debug_image_cache:
renpy.display.ic_log.write("Predict screen %s", name)
if not isinstance(name, tuple):
name = tuple(name.split())
screen = get_screen_variant(name[0])
if screen is None:
return
if _layer is None:
_layer = get_screen_layer(name)
scope = { }
scope["_scope"] = scope
if screen.parameters:
scope["_kwargs" ] = kwargs
scope["_args"] = _args
else:
scope.update(kwargs)
try:
if screen is None:
raise Exception("Screen %s is not known.\n" % (name[0],))
if not screen.predict:
return
d = ScreenDisplayable(screen, None, None, _widget_properties, scope)
d.cache = cache_get(screen, _args, kwargs)
d.update()
cache_put(screen, _args, kwargs, d.cache)
renpy.display.predict.displayable(d)
except:
if renpy.config.debug_image_cache:
import traceback
print("While predicting screen", _screen_name)
traceback.print_exc()
finally:
del scope["_scope"]
renpy.ui.reset()
def hide_screen(tag, layer=None):
"""
:doc: screens
The programmatic equivalent of the hide screen statement.
Hides the screen with `tag` on `layer`.
"""
if layer is None:
layer = get_screen_layer((tag,))
screen = get_screen(tag, layer)
if screen is not None:
renpy.exports.hide(screen.tag, layer=layer)
def use_screen(_screen_name, *_args, **kwargs):
_name = kwargs.pop("_name", ())
_scope = kwargs.pop("_scope", { })
name = _screen_name
if not isinstance(name, tuple):
name = tuple(name.split())
screen = get_screen_variant(name[0])
if screen is None:
raise Exception("Screen %r is not known." % (name,))
old_transfers = _current_screen.old_transfers
_current_screen.old_transfers = True
if screen.parameters:
scope = { }
scope["_kwargs"] = kwargs
scope["_args"] = _args
else:
scope = _scope.copy()
scope.update(kwargs)
scope["_scope"] = scope
scope["_name"] = (_name, name)
try:
screen.function(**scope)
finally:
del scope["_scope"]
_current_screen.old_transfers = old_transfers
def current_screen():
return _current_screen
def get_widget(screen, id, layer=None): # @ReservedAssignment
"""
:doc: screens
From the `screen` on `layer`, returns the widget with
`id`. Returns None if the screen doesn't exist, or there is no
widget with that id on the screen.
"""
if isinstance(screen, ScreenDisplayable):
screen = screen.screen_name
if screen is None:
screen = current_screen()
else:
if layer is None:
layer = get_screen_layer(screen)
screen = get_screen(screen, layer)
if not isinstance(screen, ScreenDisplayable):
return None
if screen.child is None:
screen.update()
rv = screen.widgets.get(id, None)
return rv
def get_widget_properties(id, screen=None, layer=None): # @ReservedAssignment
"""
:doc: screens
Returns the properties for the widget with `id` in the `screen`
on `layer`. If `screen` is None, returns the properties for the
current screen. This can be used from Python or property code inside
a screen.
Note that this returns a dictionary containing the widget properties,
and so to get an individual property, the dictionary must be accessed.
"""
if screen is None:
s = current_screen()
else:
if layer is None:
layer = get_screen_layer(screen)
s = get_screen(screen, layer)
if s is None:
return { }
rv = s.widget_properties.get(id, None)
if rv is None:
rv = { }
return rv
def before_restart():
"""
This is called before Ren'Py restarts to put the screens into restart
mode, which prevents crashes due to variables being used that are no
longer defined.
"""
for k, layer in renpy.display.interface.old_scene.iteritems():
if k is None:
continue
for i in layer.children:
if isinstance(i, ScreenDisplayable):
i.restarting = True
def show_overlay_screens(suppress_overlay):
"""
Called from interact to show or hide the overlay screens.
"""
show = not suppress_overlay
if renpy.store._overlay_screens is None:
show = show
elif renpy.store._overlay_screens is True:
show = True
else:
show = False
if show:
for i in renpy.config.overlay_screens:
if get_screen(i) is None:
show_screen(i)
else:
for i in renpy.config.overlay_screens:
if get_screen(i) is not None:
hide_screen(i)
def per_frame():
"""
Called from interact once per frame to invalidate screens we want to
update once per frame.
"""
for i in renpy.config.per_frame_screens:
s = get_screen(i)
if s is None:
continue
updated_screens.discard(s)
renpy.display.render.invalidate(s)
s.update()