Passed
Pull Request — master (#21)
by Matt
03:47 queued 01:54
created

PyDMXControl.design._screen.Screen.__events()   B

Complexity

Conditions 7

Size

Total Lines 21
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 18
nop 1
dl 0
loc 21
rs 8
c 0
b 0
f 0
1
"""
2
 *  PyDMXControl: A Python 3 module to control DMX using uDMX.
3
 *                Featuring fixture profiles, built-in effects and a web control panel.
4
 *  <https://github.com/MattIPv4/PyDMXControl/>
5
 *  Copyright (C) 2018 Matt Cowley (MattIPv4) ([email protected])
6
"""
7
8
import os.path  # User files
9
from datetime import datetime  # Dates
10
from math import ceil, floor  # Rounding
11
12
import pygame  # PyGame
13
14
15
class Screen:
16
17
    def __init__(self):
18
        self.__thread = None
19
        self.__running = False
20
        self.__grid = False
21
        self.__parts = []
22
        self.__bg = (245, 245, 245)
23
        self.__title = "PyDMXControl Design"
24
        self.block_size = 21
25
26
        # Init pygame
27
        pygame.init()
28
29
        # Get font
30
        self.__font = pygame.font.SysFont("monospace", 15)
31
32
        # Create pygame screen
33
        display_info = pygame.display.Info()
34
        self.last_parts = None
35
        self.last_render = None
36
        self.last_out = None
37
        self.screen = pygame.display.set_mode((display_info.current_w - 20, display_info.current_h - 70))
38
        pygame.display.set_caption(self.__title)
39
40
    def add_part(self, part: 'Part'):
41
        self.__parts.append(part)
42
43
    def __events(self):
44
        for e in pygame.event.get():
45
            if e.type == pygame.KEYDOWN:
46
                # if e.key == pygame.K_g:
47
                #    self.__grid = not self.__grid
48
                if e.key == pygame.K_q:
49
                    self.__running = False
50
                if e.key == pygame.K_s:
51
                    if self.last_parts:
52
                        surface = self.last_parts
53
                        if pygame.key.get_mods() & pygame.KMOD_SHIFT:
54
                            temp_surface = pygame.Surface(surface.get_size(), pygame.SRCALPHA, 32)
55
                            temp_surface.convert_alpha()
56
                            temp_surface.fill(self.__bg)
57
                            temp_surface.blit(surface, (0, 0))
58
                            surface = temp_surface
59
                        file = "{}/PYDMXControl_Design_{}.png".format(
60
                            os.path.expanduser("~/Desktop"),
61
                            datetime.now().strftime("%Y%m%d_%H%M%S")
62
                        )
63
                        pygame.image.save(surface, file)
64
65
    def __draw_parts(self):
66
        # Get render of each
67
        parts = []
68
        for part in self.__parts:
69
            try:
70
                parts.append(part.design_render(self))
71
            except Exception as e:
72
                print(e)
73
74
        # Generate size
75
        maxx = max([f[0] + f[2].get_width() for f in parts])
76
        maxy = max([f[1] + f[2].get_height() for f in parts])
77
        surface = pygame.Surface((maxx, maxy), pygame.SRCALPHA, 32)
78
        surface.convert_alpha()
79
80
        # Render
81
        for part in parts:
82
            surface.blit(part[2], (part[0], part[1]))
83
84
        # Save full res
85
        self.last_parts = surface
86
87
    def __draw_bg(self):
88
        # New surface
89
        sx, sy = self.last_parts.get_size()
90
        surface = pygame.Surface((sx, sy), pygame.SRCALPHA, 32)
91
        surface.convert_alpha()
92
        surface.fill(self.__bg)
93
94
        # Draw grid
95
        if self.__grid:
96
            w, h = pygame.display.get_surface().get_size()
97
            w = ceil(w / self.block_size)
98
            h = ceil(h / self.block_size)
99
            for y in range(h):
100
                for x in range(w):
101
                    invert = ((x + (y % 2)) % 2 == 0)
102
                    color = [200] * 3 if invert else [250] * 3
103
                    rect = pygame.Rect(x * self.block_size, y * self.block_size, self.block_size, self.block_size)
104
                    pygame.draw.rect(surface, color, rect)
105
106
        # Add parts
107
        surface.blit(self.last_parts, (0, 0))
108
109
        # Save full res
110
        self.last_render = surface
111
112
    def __draw_mouse(self):
113
        # Generate text
114
        mx, my = pygame.mouse.get_pos()
115
        text = self.__font.render("x: {:,} y: {:,}".format(
116
            floor(mx / self.block_size),
117
            floor(my / self.block_size),
118
        ), True, (0, 0, 0))
119
120
        # Generate background
121
        padding = 5
122
        x, y = text.get_size()
123
        surface = pygame.Surface((x + padding * 2, y + padding * 2), pygame.SRCALPHA, 32)
124
        surface = surface.convert_alpha()
125
        surface.fill((225, 225, 225, 128))
126
127
        # Add text
128
        surface.blit(text, (padding, padding))
129
130
        # Add border
131
        x, y = surface.get_size()
132
        rectv = pygame.Rect(0, y / 4 * 3, int(floor(padding / 2)), y / 4)
133
        pygame.draw.rect(surface, (0, 0, 0), rectv)
134
        recth = pygame.Rect(0, y - int(floor(padding / 2)), y / 4, int(floor(padding / 2)))
135
        pygame.draw.rect(surface, (0, 0, 0), recth)
136
137
        # Render
138
        self.screen.blit(surface, (mx, my - y))
139
140
    def __draw(self, redraw: int) -> int:
141
        # Only regenerate content every 100 cycles
142
        if redraw % 100 == 0:
143
            # Draw parts
144
            self.__draw_parts()
145
146
            # Draw base
147
            self.__draw_bg()
148
149
            # Draw mouse
150
            # self.__draw_mouse()
151
152
            # Resize if bigger than screen
153
            mx, my = self.last_render.get_size()
154
            sx, sy = self.screen.get_size()
155
            if mx > sx:
156
                my = my * (sx / mx)
157
                mx = sx
158
            if my > sy:
159
                mx = mx * (sy / my)
160
                my = sy
161
            self.last_out = pygame.transform.scale(self.last_render, (floor(mx), floor(my)))
162
163
            # Reset redraw
164
            redraw = -1
165
166
        # Render
167
        self.screen.fill(self.__bg)
168
        self.screen.blit(self.last_out, (0, 0))
169
170
        return redraw
171
172
    def __start(self):
173
        self.clock = pygame.time.Clock()
174
175
        # Draw to screen
176
        redraw = self.__draw(0) + 1
177
178
        while self.__running:
179
            # Handle events
180
            self.__events()
181
182
            # Draw to screen (every 10 cycles)
183
            if redraw % 10 == 0:
184
                pygame.display.set_caption(self.__title)
185
                redraw = self.__draw(redraw)
186
            redraw += 1
187
188
            # Update display
189
            self.clock.tick(60)
190
            pygame.display.flip()
191
192
        pygame.quit()
193
194
    def render(self):
195
        self.__running = True
196
        self.__start()
197
        quit()
198