Files
2020-07-29 12:59:12 +02:00

576 lines
18 KiB
Python

from .manager import Manager
from .rectangle import Rectangle
from .menu import Button as mButton
from .abstract import Segment, Circle, Point
from . import colors
from functools import reduce
from pygame.locals import *
import operator
import pygame
import random
old_styles = {
"text": {
"default": colors.WHITE,
"hovered": colors.WHITE,
"focused": colors.WHITE,
"clicked": colors.WHITE},
"background": {
"default": None,
"hovered": None,
"focused": None,
"clicked": None},
"bordure": {
"default": colors.WHITE,
"hovered": colors.WHITE,
"focused": colors.WHITE,
"clicked": colors.WHITE}}
class Widget:
default = 0
hovered = 1
focus = 2
clicked = 3
def __init__(self, state=0, reference=[]):
"""Create a widget."""
self.state = state
self.reference = reference
def update(self):
"""Update the button."""
self.updateColors()
def reactKeyDown(self, key):
"""React to a keydown event."""
if key == K_RETURN:
self.state = 0
def reactMouseMotion(self, position):
"""React to a mouse motion event."""
if position in self:
if self.state == 0:
self.state = 1
elif self.state == 3:
self.state = 2
elif self.state != 2:
self.state = 0
def reactMouseButtonDown(self, button, position):
"""React to a mouse button down event."""
if button == 1:
if position in self:
if self.state >= 2:
self.state = 0
else:
self.state = 3
else:
self.state = 0
@property
def default(self):
"""Determine if the button is inactive."""
return self.state == 0
@property
def hovered(self):
"""Determine if the button is hovered."""
return self.state == 1
@property
def focused(self):
"""Determine if the button is focused."""
return self.state == 2
@property
def clicked(self):
"""Determine if the button is clicked."""
return self.state == 3
class Slider(Segment, Widget):
@classmethod
def random(cls, **kwargs):
"""Return a random slider."""
s = Segment.random(**kwargs)
return cls(*s.points)
def __init__(self, *points,
borns=[0, 1],
relative_value=0,
radius=1,
state=0,
error=1e-2,
round_precision=2,
styles=[
[colors.WHITE, colors.GREEN, colors.BLUE, colors.RED], # Segment
[colors.WHITE, colors.GREEN, colors.BLUE, colors.RED], # Cursor bordure
[colors.BLACK for i in range(4)] # Cursor area
],
**kwargs):
"""Create a slider."""
super().__init__(*points, **kwargs)
self.state = state
self.borns = borns
self.relative_value = relative_value
self.radius = radius
self.styles = styles
self.error = error
self.round_precision = round_precision
self.update()
def __contains__(self, position):
"""Determine if the position is on the slider."""
b1 = Segment.__contains__(self, position, e=self.error)
b2 = (position in self.cursor)
return b1 or b2
def update(self):
"""Update the colors."""
self.updateColors()
def reactMouseMotion(self, position):
"""React to a mouse motion event."""
super().reactMouseMotion(position)
if self.state >= 2:
self.updateValue(position)
def updateValue(self, position, ):
"""Update the value."""
l = self.line
p = l.projectPoint(Point(*position))
s = Segment(self.p1, p)
if abs(s.angle - self.angle) <= self.error:
length = s.length
else:
length = -s.length
self.relative_value = length / self.length
self.relative_value = max(self.relative_value, 0)
self.relative_value = min(self.relative_value, 1)
def updateColors(self):
"""Update the colors for the slider."""
self.color = self.styles[0][self.state]
self.circle_color = self.styles[1][self.state]
self.circle_area_color = self.styles[2][self.state]
def show(self, context):
"""Show the slider on screen."""
self.showSegments(context)
self.showCursor(context)
self.showValue(context)
def showSegments(self, context):
"""Show 2 segments instead of 1 to avoid crossing the circle of the
cursor."""
s1, s2 = self.segments
s1.show(context)
s2.show(context)
def showValue(self, context):
"""Show the actual value of the slider."""
context.print(str(round(self.value, self.round_precision)), \
self.middle.position)
def showCursor(self, context):
"""Show the cursor of the slider."""
self.cursor.show(context)
@property
def segments(self):
"""Return 2 segments instead of 1 to avoid crossing the circle of the
cursor."""
rv = self.relative_value
r = self.radius
l = self.length
x1 = max(rv - r / l, 0)
x2 = min(rv + r / l, 1)
p1 = self(x1)
p2 = self(x2)
s1 = Segment(self.p1, p1, color=self.color)
s2 = Segment(self.p2, p2, color=self.color)
return (s1, s2)
@property
def cursor(self):
"""Return the point associated with the cursor."""
return Circle(*self(self.relative_value), radius=self.radius,
color=self.circle_color, area_color=self.circle_area_color)
def getValue(self):
"""Return the absolute value using the borns and the relative value."""
a, b = self.borns
return a + self.relative_value * (b - a)
def setValue(self, value):
"""Set the absolute value using the borns and the relative value."""
a, b = self.borns
self.relative_value = (value - a) / (b - a)
value = property(getValue, setValue)
class Button(Rectangle, Widget):
"""
States of the Buttons:
- 0 for default
- 1 for hover
- 2 for focus
- 3 for click
"""
@classmethod
def random(cls, **kwargs):
"""Return a random button."""
r = Rectangle.random()
return cls(r.position, r.size, **kwargs)
def __init__(self,
position,
size,
text="Button",
text_size=1,
police="monospace",
bold=False,
italic=False,
centered=True,
converted=True,
state=0,
styles=[
[colors.WHITE, colors.GREEN, colors.BLUE, colors.RED], # Text
[colors.WHITE, colors.GREEN, colors.BLUE, colors.RED], # Bordure
[colors.BLACK for i in range(4)] # Background
],
**kwargs):
"""Create a button using its position, size and optional states and
color attributes."""
Rectangle.__init__(self, position, size, **kwargs)
self.state = state
self.styles = styles
self.text = text
self.text_size = text_size
self.police = police
self.bold = bold
self.italic = italic
self.centered = centered
self.converted = converted
self.updateColors()
def updateColors(self):
"""Update the color of the button depending on the state of the button."""
self.text_color = self.styles[0][self.state]
self.side_color = self.styles[1][self.state]
self.area_color = self.styles[2][self.state]
def show(self, context, **kwargs):
"""Show the button."""
super().show(context, **kwargs)
self.showText(context)
def showText(self, context):
"""Show the text of the button."""
if self.converted:
text_size = self.text_size * max(context.units)
else:
text_size = self.text_size
font = pygame.font.SysFont(self.police, int(text_size), self.bold, self.italic)
font_surface = font.render(self.text, True, self.text_color)
sfx, sfy = font_surface.get_size()
x, y = context.getToScreen(self.position)
if self.centered:
position = (x - sfx // 2, y - sfy // 2)
else:
position = (x, y)
context.draw.window.blit(font_surface, position)
class WidgetManager(Manager):
def __init__(self, widgets, **kwargs):
"""Create a widget manager."""
super().__init__(**kwargs)
self.widgets = widgets
self.updating = False
self.updateWidgets()
def reactKeyDown(self, key):
"""React to a keydown event."""
super().reactKeyDown(key)
for widget in self.widgets:
widget.reactKeyDown(key)
self.updating = True
def reactMouseMotion(self, position):
"""React to a mouse motion event."""
position = self.context.getFromScreen(tuple(position))
for widget in self.widgets:
widget.reactMouseMotion(position)
self.updating = True
def reactMouseButtonDown(self, button, position):
"""React to a mouse button down event."""
position = self.context.getFromScreen(tuple(position))
for widget in self.widgets:
widget.reactMouseButtonDown(button, position)
self.updating = True
def update(self):
"""Update the widgets when necessary."""
if self.updating:
self.updateWidgets()
self.updating = False
def updateWidgets(self):
"""Update all widgets."""
for widget in self.widgets:
widget.update()
def show(self):
"""Show all widgets."""
for widget in self.widgets:
widget.show(self.context)
class WidgetManager2(Manager):
def __init__(self, widgets={}, **kwargs):
"""Create the widgets manager using the widgets and manager arguments."""
super().__init__(**kwargs)
self.widgets = widgets
self.updating = False
self.updateWidgets()
def addSlider(self, ref, borns, position=(10, 10), size=(0, 10), conversion=False):
"""Add a slider of NOTHING AT ALL."""
self.monitor[ref] = Slider(position, size, borns)
def updateWidgets(self):
"""Check for each widgets if the values they are binded to changed of
state and update them if so."""
for (reference, widget) in self.widgets.items():
value = self.read(reference)
widget.value = value
def updateWidgetsValue(self):
"""Check for each widgets if the values they are binded to changed of
state and update them if so."""
for (reference, widget) in self.widgets.items():
self.write(reference, widget.value)
def update(self):
"""Update the widgets."""
if self.updating:
self.updateWidgets()
self.updating = False
def show(self):
"""Show the widgets."""
for widget in self.widgets.values():
widget.show(self.context)
def reactKeyDown(self, key):
"""React to a keydown event."""
super().reactKeyDown(key)
for widget in self.widgets.values():
widget.reactKeyDown(key)
self.updating = True
def reactMouseMotion(self, position):
"""React to a mouse motion event."""
position = self.context.getFromScreen(tuple(position))
for widget in self.widgets.values():
widget.reactMouseMotion(position)
self.updating = True
def reactMouseButtonDown(self, button, position):
"""React to a mouse button down event."""
position = self.context.getFromScreen(tuple(position))
for widget in self.widgets.values():
widget.reactMouseButtonDown(button, position)
self.updating = True
def write(self, reference, value):
"""Write the given value into the variable of the given reference."""
pass
def read(self, reference):
"""Return the value of the variable of the given reference."""
return reduce(operator.getitem, self, path)
class WidgetTester:
"""Create a alphabet of buttons."""
@classmethod
def createAlphabetButtons(cls):
"""Create an alphabet of buttons."""
buttons = [Button((i, 0), (0.9, 0.9), letter) for (i, letter) in \
enumerate("abcdefghijklmnopqrstuvwxyz")]
return cls(buttons)
@classmethod
def createSliders(cls, n=10):
"""Create some random sliders."""
sliders = [Slider.createFromTuples((-2 * i, -10), (-2 * i, 10)) for i in range(0, n)]
return cls(sliders)
@classmethod
def createSlidersAndButtons(cls, nsliders=5, nbuttons=5, sparse=10):
"""Create some sliders and some buttons."""
sliders = [Slider.createFromTuples((-2 * i, -10), (-2 * i, 10), borns=[-2, i]) \
for i in range(0, nsliders)]
buttons = [Button((i, 0), (0.9, 0.9), letter) \
for (i, letter) in enumerate("abcdefghijklmnopqrstuvwxyz")]
return cls(sliders + buttons)
@classmethod
def createRandomSlidersAndButtons(cls, nsliders=5, nbuttons=5, sparse=10):
"""Create some sliders and some buttons."""
r = lambda: random.random()
sliders = [Slider.createFromTuples((2 * i + r(), r()), (i + r(), r())) for i in range(nsliders)]
buttons = [Button((i + 10 * r(), 10 * r()), (r(), r())) for i in range(nbuttons)]
return cls(sliders + buttons)
class Menu:
@classmethod
def createFromNames(cls, *names, size=(1, 1), sparse=1, **kwargs):
"""Create a menu using the names of the buttons."""
buttons = [Button((0, -i*sparse), size, text=names[i], **kwargs) for i in range(len(names))]
return cls(buttons)
@classmethod
def createYesNo(cls, yes="yes", no="no", **kwargs):
"""Create a yes no menu."""
yes = Button((0, 0), (1, 1), text=yes, **kwargs)
no = Button((0, -1), (1, 1), text=no, **kwargs)
return cls([yes, no])
@classmethod
def random(cls, n=5, **kwargs):
"""Create a random menu."""
buttons = [Button.random(**kwargs) for i in range(n)]
return cls(buttons)
def __init__(self, buttons):
"""Create a menu using the list of buttons."""
self.buttons = buttons
def update(self):
"""Update all buttons."""
for button in self.buttons:
button.update()
def show(self, context):
"""Show the menu on the context."""
for button in self.buttons:
button.show(context)
def reactKeyDown(self, key):
"""React to a key down event."""
for button in self.buttons:
button.reactKeyDown(key)
def reactMouseMotion(self, position):
"""React to a mouse motion event."""
for button in self.buttons:
button.reactMouseMotion(position)
def reactMouseButtonDown(self, button_touch, position):
"""React to a mouse button down event."""
for button in self.buttons:
button.reactMouseButtonDown(button_touch, position)
@property
def focus(self):
"""Return the index of the button being focused."""
for i, button in enumerate(self.buttons):
if button.state >= 2:
return i
@property
def focusing(self):
"""Determine if there is a button being focused."""
for i, button in enumerate(self.buttons):
if button.state >= 2:
return True
return False
@property
def focused(self):
"""Return the button being focused."""
if self.focusing:
return self.buttons[self.focus]
class MenuManager(Manager):
@classmethod
def createFromNames(cls, *names, manager_parameters={}, menu_parameters={}):
"""Create a menu with given names for the buttons."""
menu = Menu.createFromNames(*names, **menu_parameters)
return cls(menu, **manager_parameters)
@classmethod
def createYesNo(cls, **kwargs):
"""Create a simple menu yes or no."""
menu = Menu.createYesNo()
return cls(menu, **kwargs)
@classmethod
def random(cls, **kwargs):
"""Create a menu manager with a random menu. """
return cls(Menu.random(), **kwargs)
def __init__(self, menu, **kwargs):
"""Create a menu manager."""
super().__init__(**kwargs)
self.menu = menu
def update(self):
"""Update the menu."""
self.menu.update()
def show(self):
"""Show the menu."""
self.menu.show(self.context)
def reactKeyDown(self, key):
"""React to a keydown event."""
super().reactKeyDown(key)
self.menu.reactKeyDown(key)
def reactMouseMotion(self, position):
"""React to a mouse motion event."""
position = self.context.getFromScreen(tuple(position))
self.menu.reactMouseMotion(position)
def reactMouseButtonDown(self, button, position):
"""React to a mouse button down event."""
position = self.context.getFromScreen(tuple(position))
self.menu.reactMouseButtonDown(button, position)
if self.menu.focusing:
self.context.console("Focus on button:", self.menu.focused.text)
else:
self.context.console("Focus unset")
class WidgetTester1(WidgetTester, WidgetManager):
"""Class of widget tester for the WidgetManager1."""
class WidgetTester2(WidgetTester, WidgetManager2):
"""Class of widget tester for the WidgetManager2."""
if __name__ == "__main__":
m=WidgetTester1.createSlidersAndButtons()
m()
# names = ["salut", "mec", "tranquille"]
# names = list("abcdefghijklmnopqrstuvwxyz")
# m = MenuManager.createFromNames(*names, menu_parameters={"size": (4, 1), "sparse": 2})
# m()