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

680 lines
22 KiB
Python

from .draw import Draw
from .rect import Rect
from .window import Window
from . import colors
import time as tm
import copy
# For the camera only
from pygame.locals import *
import numpy as np
import pygame
import cv2
# For the camera only
class Camera:
"""The camera relies on opencv, pygame and numpy."""
def __init__(self, draw, position=[0, 0],
build_capture=False,
build_screen_writer=False,
build_capture_writer=False,
filename="unnamed.mp4",
framerate=15):
"""Create an opencv camera. By default the capture is not active, it must
be built using the build method, or by setting build to True in the
init method."""
self._draw = draw
self.position = position
self.filename = filename
self.fourccs = {'mp4': 'MP4V', 'avi': 'DIVX'}
self.framerate = framerate
# Building videos components
if build_capture:
self.buildCapture()
if build_screen_writer:
self.buildScreenWriter()
if build_capture_writer:
self.buildCaptureWriter()
def buildCapture(self):
"""Build the capture with opencv. Having a separate function for the
capture allows to have more efficiency for programs that do not need the
camera."""
self.capture = cv2.VideoCapture(0)
def buildCaptureWriter(self, filename=None, framerate=None):
"""Write the videos of the camera, this is just an odd concept, nothing
really functional."""
if not filename: filename = self.filename
_, extension = filename.split('.')
fourcc = self.fourccs[extension]
print(fourcc)
fourcc = cv2.VideoWriter_fourcc(*fourcc)
if not framerate: framerate = self.framerate
self.capture_writer = cv2.VideoWriter(filename, fourcc, framerate, frameSize=self._draw.window.size)
def buildScreenWriter(self, filename=None, framerate=None):
"""Write the videos of the screen."""
if not filename: filename = self.filename
_, extension = filename.split('.')
fourcc = self.fourccs[extension]
fourcc = cv2.VideoWriter_fourcc(*fourcc)
if not framerate: framerate = self.framerate
self.screen_writer = cv2.VideoWriter(filename, fourcc, framerate, frameSize=self._draw.window.size)
def writeCapture(self):
"""Write the capture."""
# Note this cannot work if the video capture is not built
_, frame = self.capture.read()
self.capture_writer.write(frame)
def writeScreen(self):
"""Write the screen."""
output = pygame.surfarray.array3d(self._draw.window.screen)
output = cv2.transpose(output)
output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR)
self.screen_writer.write(output)
def show(self):
"""Show the video."""
ret, frame = self.capture.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = np.rot90(frame)
frame = pygame.surfarray.make_surface(frame)
self._draw.image(self._draw.window.screen, frame, self.position)
__call__ = show
def endCaptureWriter(self):
"""End the video writer and save its content."""
del self.capture_writer
def endScreenWriter(self):
"""End the screen writer and record its content."""
del self.screen_writer
def endCapture(self):
"""End the camera by ending the video capture."""
del self.capture
def destroy(self):
"""Destroy all cv2 windows."""
cv2.destroyAllWindows()
def isCapturing(self):
"""Determine if the capture is set."""
return "capture" in self.__dict__
def isWriting(self):
"""Determine if some content is being recorded."""
return ("screen_writer" in self.__dict__) or ("capture_writer" in self.__dict__)
def isScreenWriting(self):
"""Determine if the screen is being recorded."""
return "screen_writer" in self.__dict__
def isCaptureWriting(self):
"""Determine if the capture is being recorded."""
return "capture_writer" in self.__dict__
def setCapturing(self, capturing):
"""Set the capturing to the boolean 'capturing'."""
if not self.capturing and capturing:
self.buildCapture()
if self.capturing and not capturing:
self.endCapture()
def setScreenWriting(self, screen_writing):
"""Set the screen writing to the boolean 'screen_writing'."""
if not self.screen_writing and screen_writing:
self.buildScreenWriter()
if self.screen_writing and not screen_writing:
self.endScreenWriter()
def setCaptureWriting(self, capture_writing):
"""Set the capture writing to the boolean 'capture_writing'."""
if not self.capture_writing and capture_writing:
self.buildCaptureWriter()
if self.capture_writing and not capture_writing:
self.endCaptureWriter()
capturing = property(isCapturing, setCapturing)
screen_writing = property(isScreenWriting)
capture_writing = property(isCaptureWriting)
writing = property(isWriting)
def switchCapture(self):
"""Switch the capture mode."""
if self.capturing:
self.endCapture()
else:
self.buildCapture()
def switchScreenWriting(self):
"""Switch the screen writing mode."""
if self.screen_writing:
self.endScreenWriter()
else:
self.buildScreenWriter()
def switchCaptureWriting(self):
"""Switch the capture writing mode."""
if self.capture_writing:
self.endCaptureWriter()
else:
self.buildCaptureWriter()
def __del__(self):
"""Delete the camera components if they exist."""
if self.capturing:
del self.capture
if self.screen_writing:
del self.screen_writer
if self.capture_writing:
del self.capture_writer
self.destroy()
def write(self):
"""Write the screen or the capture video if they are on."""
if self.screen_writing:
self.writeScreen()
if self.capture_writing:
self.writeCapture()
def release(self):
"""Release the screen or the capture video if they are on."""
if self.screen_writing:
self.screen_writer.release()
if self.capture_writing:
self.capture_writer.release()
class Line:
"""Representation of a line in the console.
This might be used for visual debugging but also for typing commands."""
def __init__(self, *content, time=None, color=colors.WHITE, separator=" "):
"""Object of line created using the text and optional time."""
if time is None: time=tm.time()
self.content = list(content)
self.time = time
self.color = color
self.separator = separator
def __str__(self):
"""Return the string representation of a line."""
return self.separator.join(map(str, self.content))
def __iter__(self):
"""Iterate a line by giving its content of the instant the line was written."""
self.iterator = 0
return self
def __next__(self):
"""Return first the content, then the instant the line was written."""
if self.iterator < 2:
self.iterator += 1
if self.iterator + 1 == 0:
return self.content
elif self.iterator + 1 == 1:
return self.time
def getText(self):
"""Return the content of a line."""
return self.content
text = property(getText)
def refresh(self):
"""Refresh the time."""
self.time = tm.time()
@property
def empty(self):
return self.content[0] == "" # difficult to do uglier
class Console:
def __init__(self, draw,
lines=[],
interline=15,
left_padding=10,
down_padding=20,
conversion=False,
size=20,
position=[0, 0],
font=None,
max_lines_shown=10,
duration_lines_shown=10,
disappearance_lines_shown=10,
colors=[colors.WHITE, colors.BLACK]):
"""Create a console."""
self._draw = draw # Draw is protected and can only be read
self.lines = lines # List of Lines
self.interline = interline
self.left_padding = left_padding
self.down_padding = down_padding
self.conversion = conversion
self.size = size
self.position = position
self.font = font
self.max_lines_shown = max_lines_shown
self.duration_lines_shown = duration_lines_shown
self.disappearance_lines_shown = disappearance_lines_shown
self.colors = colors
self.line_index = 0
self.line_memory = None
def eval(self):
"""Execute the last line."""
content = self.lines[-1].content
if len(content) >= 1:
if content[0] == "marc":
content[0] += " is awesome."
elif content[0] == "time":
content[0] = "The time is " + time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) + "."
elif content[0] == "minecraft":
content[0] += "is the best game ever."
elif content[0][:6] == "print(":
self(eval(content[0][6:-1]))
elif content[0] == "refresh":
self.refresh()
def back(self):
"""Set the line to an older one."""
if self.line_index == 0:
self.line_memory = copy.deepcopy(self.line)
if self.line_index < len(self.lines) - 1:
self.line_index += 1
self.line = copy.deepcopy(self.lines[-1 - self.line_index])
self.line.refresh()
def forward(self):
"""Set the line to a new one."""
if self.line_index > 1:
self.line_index -= 1
self.line = copy.deepcopy(self.lines[-1 - self.line_index])
elif self.line_index == 1:
self.line_index -= 1
self.line = self.line_memory
self.line.refresh()
def clear(self):
"""Clear the console by removing all the lines."""
self.lines = []
def refresh(self):
"""Refresh the console by showing its previous messages."""
for line in self.lines:
line.time = tm.time()
def __getitem__(self, i):
"""Return the i-th line."""
return self.lines[i]
def __setitem__(self, v, i):
"""Set the i-th line."""
self.lines[i] = v
def __iadd__(self, *args):
"""Add a line to the console."""
l = Line(*args, time=tm.time())
self.lines.append(l)
append = __iadd__
def appendLines(self, lines):
"""Append multiple lines."""
for line in lines:
self.append(line)
def __call__(self, *args, **kwargs):
"""Display a message on the context as a console would do."""
if len(args) > 0:
l = Line(*args, **kwargs)
self.lines.append(l)
def nextArg(self):
"""Create a new argument."""
self.lines[-1].content.append("")
def nextLine(self):
"""Create a new line."""
self("")
def setLine(self, line):
self.lines[-1] = line
def getLine(self):
return self.lines[-1]
def delLine(self):
del self.lines[-1]
line = property(getLine, setLine, delLine)
def show(self):
"""Show the console without adding a text."""
sx, sy = self._draw.window.size
n = len(self.lines)
nmax = self.max_lines_shown
dls = self.disappearance_lines_shown
for i in range(min(n, nmax)):
position = (self.left_padding, sy - self.interline * i - self.down_padding)
to = self.lines[-i - 1].time
t = (tm.time() - to) / self.duration_lines_shown
if t < 1:
c1 = self.lines[-i-1].color + (t ** dls,)
# c2 = self.colors[1] + (t ** dls,)
self._draw.print(self.lines[-i - 1], position, self.size, c1, self.font, self.conversion)
class Context(Rect):
"""The context is an object that allows the user to display graphical objects
on the screen in a virtual mathematical plane."""
def createFromSizeAndCorners(size, corners, **kwargs):
c = Context(size=size, **kwargs)
c.corners = corners
return c
def __init__(self, draw=None, console=None, camera=None, **kwargs):
"""Create a context using the optional draw, console and camera, with
some of the draw's arguments for the window.
If those are not given, they will be created automatically."""
if draw is None:
draw = Draw(**kwargs)
if console is None:
console = Console(draw)
if camera is None:
camera = Camera(draw)
self.start_time = tm.time()
self.draw = draw
self.console = console
self.camera = camera
self.clear = self.draw.clear
self.flip = self.draw.window.flip
self.press = self.draw.window.press
self.build = self.draw.window.build
self.click = self.draw.window.click
self.__call__ = self.draw.window.__call__
self.wait = self.draw.window.wait
self.control = self.draw.control
self.checking = self.draw.window.checking
self.loadImage = self.draw.window.loadImage
self.print = self.draw.print
self.update = self.draw.window.update
self.switch = self.draw.window.switch
self.count = self.draw.window.count
self.alert = self.draw.window.alert
self.scale = self.draw.window.scale
def getScreen(self):
return self.draw.window.screen
def setScreen(self, screen):
self.draw.window.screen = screen
screen = property(getScreen, setScreen)
def getKeys(self):
"""Return the keys of the window."""
return self.press()
def getCorners(self):
"""Return the corners of the surface shown."""
return self.draw.plane.getCorners(self.draw.window)
def setCorners(self, corners):
"""Set the corners of the plane using the new corners."""
self.draw.plane.setCorners(corners, self.draw.window)
def getRect(self):
"""Return the rect of the surface shown."""
return Plane.getRectFromCorners(self.getCorners())
def setRect(self, rect):
"""Set the rect of the surface shown."""
self.draw.plane.setCorners(Plane.getCornersFromRect(rect))
def getCoordonnates(self):
"""Return the coordonnates of the surface shown."""
return Plane.getCoordonnatesFromCorners(self.getCorners())
def setCoordonnates(self, coordonnates):
"""Set the coordonnates of the surface shown."""
self.draw.plane.setCorners(
Plane.getCornersFromCoordonnates(coordonnates))
def getPosition(self):
"""Return the position of the surface shown."""
return self.draw.plane.position
def setPosition(self, position):
"""Set the position of the surface shown."""
self.draw.plane.position = position
def getSize(self):
"""Return the size of the surface shown."""
return self.draw.window.size
def setSize(self, size):
"""Set the size of the surface shown."""
self.draw.window.size = size
def getUnits(self):
"""Return the units of the surface shown."""
return self.draw.plane.units
def setUnits(self, units):
"""Set the units of the surface shown."""
self.draw.plane.units = units
def point(self):
"""Adapt the position of the cursor in plane's coordonnates."""
x, y = self.draw.window.point()
x, y = self.draw.plane.getFromScreen([x, y], self.draw.window)
return (x, y)
def check(self):
"""Check if the surface is open."""
self.draw.window.check()
self.open = self.draw.window.open
def __call__(self):
"""Calling the surface allow the user to move on screen."""
while self.open:
self.check()
self.draw.plane.control(self.draw.window)
self.draw.window.clear()
self.draw.plane.show(self.draw.window)
self.flip()
def __contains__(self, position):
"""Determine if a position is in the context."""
return self.draw.plane.contains(position, self.draw.window)
def show(self):
"""Show the plane on screen."""
self.draw.plane.show(self.draw.window)
def refresh(self):
"""Refresh the context by clearing the screen and showing the plane."""
self.draw.window.clear()
self.draw.plane.show(self.draw.window)
def controlZoom(self):
"""Control the zoom of the surface's plane."""
self.draw.plane.controlZoom(self.draw.window)
def controlPosition(self):
"""Control the position of the surface's plane."""
self.draw.plane.controlPosition(self.draw.window)
def getFromScreen(self, position):
"""Behave like the get from screen of the plan without having to put
the window in parameter."""
return self.draw.plane.getFromScreen(position, self.draw.window)
def getToScreen(self, position):
"""Behave like the get to screen of the plan without having to put
the window in parameter."""
return self.draw.plane.getToScreen(position, self.draw.window)
def blit(self, surface, position):
"""Blit a given surface to a given position."""
size = surface.get_size()
position = self.draw.plane.getToScreen(position, self.window)
self.draw.window.screen.blit(surface, position)
def getOpen(self):
return self.draw.window.open
def setOpen(self, open):
self.draw.window.open = open
def __enter__(self):
"""Opening the context."""
self.check()
self.control()
self.clear()
self.show()
print("went in")
return self
def __exit__(self):
"""Ending the context."""
print("going out")
self.flip()
if self.open:
self.__enter__()
def getWidth(self):
"""Return the width of the window."""
return self.size[0]
def setWidth(self, width):
"""Set the width of the window."""
self.size[0] = width
def getHeight(self):
"""Return the height of the window."""
return self.size[1]
def setHeight(self, height):
"""Set the height of the window."""
self.size[1] = height
def __bool__(self):
"""Determine if the context is opened or not."""
return self.open
def getText(self):
"""Text is an alias for console and can be read."""
return self.console
def setText(self, value):
"""Text is an alias for console and can be written."""
self.console = value
def showConsole(self, *args):
"""Show the console, this function is a fix for deprecated programs."""
self.console.show(*args)
def getCounter(self):
"""Return the window counter."""
return self.draw.window.counter
def setCounter(self, coutner):
"""Set the window counter."""
self.draw.window.counter = counter
def getPlane(self):
"""Return the draw's plane."""
return self.draw.plane
def setPlane(self, plane):
"""Set the draw's plane."""
self.plane = plane
def getWindow(self):
"""Return the draw's window."""
return self.draw.window
def setWindow(self, window):
"""Set the draw's window."""
self.draw.window = window
def getFullscreen(self):
"""Determine if the context is in fullscreen mode."""
return self.draw.window.fullscreen
def setFullscreen(self, fullscreen):
"""Set the fullscreen mode."""
self.draw.window.fullscreen = fullscreen
def getResolution(self):
"""Return the resolution of the screen."""
return self.width / self.height
@property
def rate(self):
return self.counter / (tm.time() - self.start_time)
window = property(getWindow, setWindow)
plane = property(getPlane, setPlane)
corners = property(getCorners, setCorners)
rect = property(getRect, setRect)
coordonnates = property(getCoordonnates, setCoordonnates) # Coordinates
position = property(getPosition, setPosition)
size = property(getSize, setSize)
units = property(getUnits, setUnits)
keys = property(getKeys)
open = property(getOpen, setOpen)
width = property(getWidth, setWidth)
height = property(getHeight, setHeight)
text = property(getText, setText)
counter = property(getCounter, setCounter)
fullscreen = property(getFullscreen, setFullscreen)
resolution = property(getResolution)
Surface = Context # Ugly fix for deprectated programs
if __name__ == "__main__":
context = Context(name="Context test")
context.camera.buildCapture()
context.camera.buildScreenWriter("context test.mp4")
# context.camera.buildCaptureWriter('zzzz.mp4')
context.console.duration_lines_shown = 2
context.console.max_lines_shown = 40
# context.corners=[0,0,1,1]
print(context.corners)
print(bool(context))
while context:
context.count()
print(context.counter)
context.check()
context.update()
context.control()
context.clear()
context.show()
context.camera.show()
context.camera.write()
context.console(context.rate)
# if context.counter > 100:
# context.camera.destroy()
context.flip()
context.camera.capture.release()
context.camera.screen_writer.release()
print('released')