PGTKernelBasic 0.22.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Kernel/ImageObj.py +52 -0
- Kernel/KernalInit.py +21 -0
- Kernel/KernelAudio.py +97 -0
- Kernel/KernelColor.py +420 -0
- Kernel/KernelEvent.py +129 -0
- Kernel/KernelLayout.py +58 -0
- Kernel/KernelPosition.py +231 -0
- Kernel/KernelRun.py +81 -0
- Kernel/KernelWidget.py +353 -0
- Kernel/ObjType.py +14 -0
- Kernel/PgRenderCompo/ButtonObj.py +192 -0
- Kernel/PgRenderCompo/LinkRenderfunc.py +71 -0
- Kernel/PgRenderCompo/RRender.py +84 -0
- Kernel/PgRenderCompo/Render.py +78 -0
- Kernel/PgRenderCompo/TextObj.py +127 -0
- Kernel/PgRenderCompo/__init__.py +5 -0
- Kernel/RFlags.py +63 -0
- Kernel/UFlags.py +18 -0
- Kernel/VFlags.py +29 -0
- Kernel/__init__.py +15 -0
- Kernel/geometry.py +117 -0
- pgtkernelbasic-0.22.0.dist-info/METADATA +11 -0
- pgtkernelbasic-0.22.0.dist-info/RECORD +25 -0
- pgtkernelbasic-0.22.0.dist-info/WHEEL +5 -0
- pgtkernelbasic-0.22.0.dist-info/top_level.txt +1 -0
Kernel/KernelEvent.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import pygame
|
|
2
|
+
from functools import partial
|
|
3
|
+
|
|
4
|
+
from Kernel.KernelWidget import MainScreen
|
|
5
|
+
from Kernel.VFlags import *
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _handle_resize(self: "EventDispatcher"):
|
|
9
|
+
self.screen.blank()
|
|
10
|
+
for widget in list(reversed(self.screen.child.values())):
|
|
11
|
+
widget.dispatch_resize()
|
|
12
|
+
def _handle_quit(self: "EventDispatcher"):
|
|
13
|
+
return False
|
|
14
|
+
mouse_event2flags = {
|
|
15
|
+
pygame.MOUSEBUTTONUP : {
|
|
16
|
+
1: Uplclick,
|
|
17
|
+
2: Upscrollmouse,
|
|
18
|
+
3: Uprclick,
|
|
19
|
+
4: Upscrollup,
|
|
20
|
+
5: Upscrolldown,
|
|
21
|
+
},
|
|
22
|
+
pygame.MOUSEBUTTONDOWN : {
|
|
23
|
+
1 : Downlclick,
|
|
24
|
+
2 : Downscrollmouse,
|
|
25
|
+
3 : Downrclick,
|
|
26
|
+
4 : Downscrollup,
|
|
27
|
+
5 : Downscrolldown,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
special_event_to_handle = {
|
|
31
|
+
pygame.VIDEORESIZE: _handle_resize,
|
|
32
|
+
pygame.QUIT: _handle_quit,
|
|
33
|
+
}
|
|
34
|
+
def extra_parameter(handler, *args, **kwargs):
|
|
35
|
+
return partial(handler, *args, **kwargs)
|
|
36
|
+
class EventDispatcher:
|
|
37
|
+
def __init__(self, screen: MainScreen):
|
|
38
|
+
self.screen = screen
|
|
39
|
+
self.current_hovered = None
|
|
40
|
+
self.event = None
|
|
41
|
+
self.focused_widget = set()
|
|
42
|
+
self.pressed_key = set()
|
|
43
|
+
def event_passdown(self):
|
|
44
|
+
mouse_pos = pygame.mouse.get_pos()
|
|
45
|
+
for event in pygame.event.get():
|
|
46
|
+
self.event = event
|
|
47
|
+
match event.type:
|
|
48
|
+
case pygame.MOUSEBUTTONDOWN:
|
|
49
|
+
self._dispatch_mousebuttondown(mouse_pos, event)
|
|
50
|
+
case pygame.MOUSEBUTTONUP:
|
|
51
|
+
self._dispatch_mousebuttonup(mouse_pos, event)
|
|
52
|
+
case pygame.VIDEORESIZE | pygame.QUIT:
|
|
53
|
+
result_func = special_event_to_handle[event.type]
|
|
54
|
+
is_off = result_func(self)
|
|
55
|
+
if is_off is False:
|
|
56
|
+
return False
|
|
57
|
+
case pygame.TEXTINPUT:
|
|
58
|
+
for widget in self.focused_widget:
|
|
59
|
+
if hasattr(widget, 'process_addchar'):
|
|
60
|
+
widget.process_addchar(event.text)
|
|
61
|
+
case pygame.KEYDOWN:
|
|
62
|
+
self.pressed_key.add(event.key)
|
|
63
|
+
self._dispatch_key(event)
|
|
64
|
+
case pygame.KEYUP:
|
|
65
|
+
self.pressed_key.discard(event.key)
|
|
66
|
+
self._dispacth_hover(mouse_pos)
|
|
67
|
+
return True
|
|
68
|
+
def _dispatch_key(self, event):
|
|
69
|
+
match event.key:
|
|
70
|
+
case pygame.K_BACKSPACE:
|
|
71
|
+
if event.scancode == 0:
|
|
72
|
+
return
|
|
73
|
+
for widget in self.focused_widget:
|
|
74
|
+
if hasattr(widget, 'process_backspace'):
|
|
75
|
+
widget.process_backspace()
|
|
76
|
+
def _dispacth_hover(self, mouse_pos):
|
|
77
|
+
self.event = None
|
|
78
|
+
new_hovered = None
|
|
79
|
+
for widget in list(reversed(self.screen.child.values())):
|
|
80
|
+
if hasattr(widget, 'is_hovered') and widget.inrect(mouse_pos):
|
|
81
|
+
new_hovered = widget
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
if new_hovered != self.current_hovered:
|
|
85
|
+
if self.current_hovered:
|
|
86
|
+
self.current_hovered.dispatch_release(mouse_pos)()
|
|
87
|
+
self.current_hovered.is_hovered = False
|
|
88
|
+
|
|
89
|
+
if new_hovered:
|
|
90
|
+
new_hovered.dispatch_hover(mouse_pos)()
|
|
91
|
+
new_hovered.is_hovered = True
|
|
92
|
+
|
|
93
|
+
self.current_hovered = new_hovered
|
|
94
|
+
def _dispatch_mousebuttondown(self, mouse_pos, event):
|
|
95
|
+
for widget in list(reversed(self.screen.child.values())):
|
|
96
|
+
if widget.inrect(mouse_pos):
|
|
97
|
+
self.screen.focused = False
|
|
98
|
+
if hasattr(widget, 'dispatch_click'):
|
|
99
|
+
func = widget.dispatch_click(mouse_pos, event)
|
|
100
|
+
func()
|
|
101
|
+
return
|
|
102
|
+
self.screen.focused = True
|
|
103
|
+
def _dispatch_mousebuttonup(self, mouse_pos, event):
|
|
104
|
+
if not self.screen.focused:
|
|
105
|
+
for widget in list(reversed(self.screen.child.values())):
|
|
106
|
+
if widget.inrect(mouse_pos):
|
|
107
|
+
for w in list(self.focused_widget):
|
|
108
|
+
w.focused = False
|
|
109
|
+
self.focused_widget.clear()
|
|
110
|
+
|
|
111
|
+
widget.focused = True
|
|
112
|
+
self.focused_widget.add(widget)
|
|
113
|
+
if hasattr(widget, 'process_addchar'):
|
|
114
|
+
pygame.key.start_text_input()
|
|
115
|
+
else:
|
|
116
|
+
pygame.key.stop_text_input()
|
|
117
|
+
if hasattr(widget, 'dispatch_click'):
|
|
118
|
+
func = widget.dispatch_click(mouse_pos, event)
|
|
119
|
+
func()
|
|
120
|
+
|
|
121
|
+
return
|
|
122
|
+
else:
|
|
123
|
+
for w in list(self.focused_widget):
|
|
124
|
+
w.focused = False
|
|
125
|
+
if hasattr(w, 'process_addchar'):
|
|
126
|
+
pygame.key.stop_text_input()
|
|
127
|
+
self.focused_widget.clear()
|
|
128
|
+
def is_key_pressed(self, key_code):
|
|
129
|
+
return key_code in self.pressed_key
|
Kernel/KernelLayout.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from Kernel.KernelWidget import Widget, ImmutableRect
|
|
2
|
+
from Kernel.ObjType import PosTuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class GridLayout:
|
|
6
|
+
def __init__(self, width_grid: int, height_grid: int, size: PosTuple, pos: PosTuple | None = None, padding=20):
|
|
7
|
+
self.width_grid = width_grid
|
|
8
|
+
self.height_grid = height_grid
|
|
9
|
+
self.total_width, self.total_height = size
|
|
10
|
+
self.pos = pos if pos is not None else (0, 0)
|
|
11
|
+
self.padding = padding
|
|
12
|
+
|
|
13
|
+
available_width = self.total_width - (2 * padding)
|
|
14
|
+
available_height = self.total_height - (2 * padding)
|
|
15
|
+
|
|
16
|
+
self.cell_width = available_width / width_grid
|
|
17
|
+
self.cell_height = available_height / height_grid
|
|
18
|
+
self.edge_cell = (self.cell_width, self.cell_height)
|
|
19
|
+
|
|
20
|
+
self.cells = []
|
|
21
|
+
|
|
22
|
+
cur_pos_x = self.pos[0] + padding
|
|
23
|
+
|
|
24
|
+
for i in range(width_grid):
|
|
25
|
+
row_cells = []
|
|
26
|
+
cur_pos_y = self.pos[1] + padding
|
|
27
|
+
|
|
28
|
+
for j in range(height_grid):
|
|
29
|
+
cell = (cur_pos_x, cur_pos_y)
|
|
30
|
+
row_cells.append(cell)
|
|
31
|
+
cur_pos_y += self.cell_height
|
|
32
|
+
|
|
33
|
+
self.cells.append(row_cells)
|
|
34
|
+
cur_pos_x += self.cell_width
|
|
35
|
+
|
|
36
|
+
def setpos(self, widget: Widget, cell_pos=tuple[int, int], center = True):
|
|
37
|
+
xpos, ypos = cell_pos
|
|
38
|
+
cell_x, cell_y = self.cells[xpos][ypos]
|
|
39
|
+
w, h = widget.rect.w, widget.rect.h
|
|
40
|
+
|
|
41
|
+
if center:
|
|
42
|
+
final_x = cell_x + (self.cell_width - w) / 2
|
|
43
|
+
final_y = cell_y + (self.cell_height - h) / 2
|
|
44
|
+
else:
|
|
45
|
+
final_x = cell_x
|
|
46
|
+
final_y = cell_y
|
|
47
|
+
if isinstance(widget.rect, ImmutableRect):
|
|
48
|
+
widget.rect = ImmutableRect(final_x, final_y, w, h)
|
|
49
|
+
else:
|
|
50
|
+
widget.rect.x = final_x
|
|
51
|
+
widget.rect.y = final_y
|
|
52
|
+
|
|
53
|
+
class HorizontalLayout(GridLayout):
|
|
54
|
+
def __init__(self, length : int, size: PosTuple, pos : PosTuple, padding=20):
|
|
55
|
+
super().__init__(length, 1, size, pos, padding)
|
|
56
|
+
class VerticalLayout(GridLayout):
|
|
57
|
+
def __init__(self, length: int, size: PosTuple, pos: PosTuple, padding=20):
|
|
58
|
+
super().__init__(1, length, size, pos, padding)
|
Kernel/KernelPosition.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#Kernel 1(build 0.09)
|
|
2
|
+
#Pre-alpha
|
|
3
|
+
from typing import Tuple, Optional
|
|
4
|
+
from Kernel.KernalInit import init
|
|
5
|
+
from Kernel.ObjType import PosTuple, RectTuple
|
|
6
|
+
init()
|
|
7
|
+
#####
|
|
8
|
+
#Margin
|
|
9
|
+
#####
|
|
10
|
+
class Anchor:
|
|
11
|
+
topleft = 'TopLeft'
|
|
12
|
+
topcenter = 'TopCenter'
|
|
13
|
+
topright = 'TopRight'
|
|
14
|
+
centerleft = 'CenterLeft'
|
|
15
|
+
center = 'Center'
|
|
16
|
+
centerright = 'CenterRight'
|
|
17
|
+
bottomleft = 'BottomLeft'
|
|
18
|
+
bottomcenter = 'BottomCenter'
|
|
19
|
+
bottomright = 'BottomRight'
|
|
20
|
+
class Margin:
|
|
21
|
+
"""
|
|
22
|
+
Manages margins and the display position of objects on the screen.
|
|
23
|
+
This class supports:
|
|
24
|
+
- Calculating padding based on percentage or absolute value.
|
|
25
|
+
- Storing default anchors to position objects.
|
|
26
|
+
- Caching calculated position results to improve performance.
|
|
27
|
+
- Updating when the screen size changes.
|
|
28
|
+
- Returning the content area (content_rect) after padding is subtracted.
|
|
29
|
+
Attributes
|
|
30
|
+
width_screen : int
|
|
31
|
+
Current screen width.
|
|
32
|
+
height_screen : int
|
|
33
|
+
Current screen height.
|
|
34
|
+
padding : tuple[float, float]
|
|
35
|
+
Padding by pixels (x, y).
|
|
36
|
+
percentage : tuple[float, float]
|
|
37
|
+
Padding by percentage (0–100).
|
|
38
|
+
cache : dict
|
|
39
|
+
Stores default anchors.
|
|
40
|
+
cache_pos : dict
|
|
41
|
+
Caches the calculated position for the object.
|
|
42
|
+
Methods:
|
|
43
|
+
get_pos(obj, anchor):
|
|
44
|
+
Calculates the (x, y) position of the object based on the anchor.
|
|
45
|
+
Save_margin(anchor):
|
|
46
|
+
Saves the default anchor for later use.
|
|
47
|
+
Update_on_resize(screen):
|
|
48
|
+
Updates screen size and padding when resizing.
|
|
49
|
+
content_rect:
|
|
50
|
+
Returns the remaining content area after subtracting padding.
|
|
51
|
+
"""
|
|
52
|
+
__slots__ = ('width_screen', 'height_screen', 'last_screen_size', 'padding', 'percentage', 'cache', 'cache_pos')
|
|
53
|
+
def __init__(self, screen, percentage_padding: PosTuple | None = None, padding: PosTuple | None = (0, 0)):
|
|
54
|
+
self.width_screen, self.height_screen = screen.get_size()
|
|
55
|
+
self.percentage = percentage_padding
|
|
56
|
+
if self.percentage:
|
|
57
|
+
if 0 < percentage_padding[0] > 100 or 0 < percentage_padding[1] > 100:
|
|
58
|
+
raise ValueError('Your padding must in range from 0 to 100')
|
|
59
|
+
self.padding = (self.width_screen * self.percentage[0] / 100, self.height_screen * self.percentage[1] / 100)
|
|
60
|
+
self.padding = padding
|
|
61
|
+
self.cache = {}
|
|
62
|
+
self.cache_pos = {}
|
|
63
|
+
def update_padding(self, new_padding):
|
|
64
|
+
self.padding = new_padding
|
|
65
|
+
def get_pos(self, obj: PosTuple, anchor: str | None):
|
|
66
|
+
if not anchor:
|
|
67
|
+
if 'Anchor' in self.cache:
|
|
68
|
+
anchor = self.cache['Anchor']
|
|
69
|
+
|
|
70
|
+
w_o, h_o = obj
|
|
71
|
+
if (w_o, h_o, anchor) in self.cache_pos:
|
|
72
|
+
return self.cache_pos[(w_o, h_o, anchor)]
|
|
73
|
+
|
|
74
|
+
w_s, h_s = self.width_screen, self.height_screen
|
|
75
|
+
b_x, b_y = self.padding
|
|
76
|
+
left = b_x
|
|
77
|
+
center_x = (w_s - w_o) // 2
|
|
78
|
+
right = w_s - w_o - b_x
|
|
79
|
+
top = b_y
|
|
80
|
+
center_y = (h_s - h_o) // 2
|
|
81
|
+
bottom = h_s - h_o - b_y
|
|
82
|
+
match anchor:
|
|
83
|
+
case 'TopLeft':
|
|
84
|
+
pos = (left, top)
|
|
85
|
+
case 'TopCenter':
|
|
86
|
+
pos = (center_x, top)
|
|
87
|
+
case 'TopRight':
|
|
88
|
+
pos = (right, top)
|
|
89
|
+
case 'CenterLeft':
|
|
90
|
+
pos = (left, center_y)
|
|
91
|
+
case 'Center':
|
|
92
|
+
pos = (center_x, center_y)
|
|
93
|
+
case 'CenterRight':
|
|
94
|
+
pos = (right, center_y)
|
|
95
|
+
case 'BottomLeft':
|
|
96
|
+
pos = (left, bottom)
|
|
97
|
+
case 'BottomCenter':
|
|
98
|
+
pos = (center_x, bottom)
|
|
99
|
+
case 'BottomRight':
|
|
100
|
+
pos = (right, bottom)
|
|
101
|
+
case _: # Trường hợp mặc định (tương đương else)
|
|
102
|
+
raise KeyError(f'Invalid anchor: {anchor}')
|
|
103
|
+
|
|
104
|
+
self.cache_pos[(w_o, h_o, anchor)] = pos
|
|
105
|
+
return pos
|
|
106
|
+
def save_margin(self, anchor: str):
|
|
107
|
+
if anchor in {
|
|
108
|
+
'CenterRight': 0,
|
|
109
|
+
'Center': 0,
|
|
110
|
+
'CenterLeft' : 0,
|
|
111
|
+
'TopCenter': 0,
|
|
112
|
+
'TopLeft' : 0,
|
|
113
|
+
'TopRight': 0,
|
|
114
|
+
'BottomCenter': 0,
|
|
115
|
+
'BottomLeft': 0,
|
|
116
|
+
'BottomRight': 0
|
|
117
|
+
}:
|
|
118
|
+
self.cache['Anchor'] = anchor
|
|
119
|
+
else:
|
|
120
|
+
raise KeyError(f'Invalid anchor: {anchor}')
|
|
121
|
+
def update_on_resize(self, screen):
|
|
122
|
+
self.width_screen, self.height_screen = screen.get_size()
|
|
123
|
+
if self.percentage:
|
|
124
|
+
self.padding = (self.width_screen * self.percentage[0] / 100,
|
|
125
|
+
self.height_screen * self.percentage[1] / 100)
|
|
126
|
+
self.cache_pos.clear()
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def content_rect(self):
|
|
130
|
+
left, top = self.padding
|
|
131
|
+
width = self.width_screen - 2 * self.padding[0]
|
|
132
|
+
height = self.height_screen - 2 * self.padding[1]
|
|
133
|
+
width = max(0, width)
|
|
134
|
+
height = max(0, height)
|
|
135
|
+
return left, top, width, height
|
|
136
|
+
|
|
137
|
+
######
|
|
138
|
+
#NEXT
|
|
139
|
+
######
|
|
140
|
+
class LayoutHelper:
|
|
141
|
+
"""
|
|
142
|
+
Docstring for LayoutHelper:
|
|
143
|
+
A utility class to calculate the position of a new object relative to an existing object's rectangle.
|
|
144
|
+
It supports positioning in four directions: 'Right', 'Left', 'Down', and 'Up', with optional padding.
|
|
145
|
+
Attributes:
|
|
146
|
+
screen_w : int
|
|
147
|
+
Current screen width.
|
|
148
|
+
screen_h : int
|
|
149
|
+
Current screen height.
|
|
150
|
+
Methods:
|
|
151
|
+
update_screen(screen):
|
|
152
|
+
Updates the stored screen dimensions.
|
|
153
|
+
Get_pos(obj_rect, next_obj_size, direction, padding=(0, 0)):
|
|
154
|
+
Calculates the position for the new object based on the specified direction and padding.
|
|
155
|
+
Getpos_up(obj_rect, next_obj_size, padding=(0, 0)):
|
|
156
|
+
Calculates the position above the existing object.
|
|
157
|
+
Getpos_down(obj_rect, next_obj_size, padding=(0, 0)):
|
|
158
|
+
Calculates the position below the existing object.
|
|
159
|
+
Getpos_right(obj_rect, next_obj_size, padding=(0, 0)):
|
|
160
|
+
Calculates the position to the right of the existing object.
|
|
161
|
+
Getpos_left(obj_rect, next_obj_size, padding=(0, 0)):
|
|
162
|
+
Calculates the position to the left of the existing object.
|
|
163
|
+
"""
|
|
164
|
+
__slots__ = ('screen_w', 'screen_h')
|
|
165
|
+
def __init__(self, screen):
|
|
166
|
+
self.screen_w, self.screen_h = screen.get_size()
|
|
167
|
+
def update_screen(self, screen):
|
|
168
|
+
self.screen_w, self.screen_h = screen.get_size()
|
|
169
|
+
|
|
170
|
+
def get_pos(self, obj_rect: RectTuple, next_obj_size: PosTuple, direction, padding=(0, 0)) -> tuple[float, float]:
|
|
171
|
+
ox, oy, ow, oh = obj_rect
|
|
172
|
+
nw, nh = next_obj_size
|
|
173
|
+
px, py = padding
|
|
174
|
+
sw, sh = self.screen_w, self.screen_h
|
|
175
|
+
|
|
176
|
+
match direction:
|
|
177
|
+
case 'Right':
|
|
178
|
+
x, y = ox + ow + px, oy + py
|
|
179
|
+
case 'Left':
|
|
180
|
+
x, y = ox - px - nw, oy + py
|
|
181
|
+
case 'Down':
|
|
182
|
+
x, y = ox + px, oy + oh + py
|
|
183
|
+
case 'Up':
|
|
184
|
+
x, y = ox + px, oy - py - nh
|
|
185
|
+
case _:
|
|
186
|
+
raise KeyError('Invalid direction')
|
|
187
|
+
|
|
188
|
+
if x < 0 or y < 0 or (x + nw > sw) or (y + nh > sh):
|
|
189
|
+
raise ValueError('Out of screen')
|
|
190
|
+
|
|
191
|
+
return x, y
|
|
192
|
+
def getpos_up(self, obj_rect: RectTuple, next_obj_size: PosTuple, padding=(0, 0))-> tuple[float, float]:
|
|
193
|
+
ox, oy, ow, oh = obj_rect
|
|
194
|
+
nw, nh = next_obj_size
|
|
195
|
+
px, py = padding
|
|
196
|
+
sw, sh = self.screen_w, self.screen_h
|
|
197
|
+
x = ox + px
|
|
198
|
+
y = oy - py - nh
|
|
199
|
+
if y < 0 or x > sw - nw or x < 0:
|
|
200
|
+
raise ValueError('Out of screen')
|
|
201
|
+
return x, y
|
|
202
|
+
def getpos_down(self, obj_rect: RectTuple, next_obj_size: PosTuple, padding=(0, 0))-> tuple[float, float]:
|
|
203
|
+
ox, oy, ow, oh = obj_rect
|
|
204
|
+
nw, nh = next_obj_size
|
|
205
|
+
px, py = padding
|
|
206
|
+
sw, sh = self.screen_w, self.screen_h
|
|
207
|
+
x = ox + px
|
|
208
|
+
y = oy + oh + py
|
|
209
|
+
if y > sh - nh or x > sw - nw or x < 0:
|
|
210
|
+
raise ValueError('Out of screen')
|
|
211
|
+
return x, y
|
|
212
|
+
def getpos_right(self, obj_rect: RectTuple, next_obj_size: PosTuple, padding=(0, 0))-> tuple[float, float]:
|
|
213
|
+
ox, oy, ow, oh = obj_rect
|
|
214
|
+
nw, nh = next_obj_size
|
|
215
|
+
px, py = padding
|
|
216
|
+
sw, sh = self.screen_w, self.screen_h
|
|
217
|
+
x = ox + ow + px
|
|
218
|
+
y = oy + py
|
|
219
|
+
if x + nw > sw or y + nh > sh or y < 0:
|
|
220
|
+
raise ValueError('Out of screen')
|
|
221
|
+
return x, y
|
|
222
|
+
def getpos_left(self, obj_rect: RectTuple, next_obj_size: PosTuple, padding=(0, 0))-> tuple[float, float]:
|
|
223
|
+
ox, oy, ow, oh = obj_rect
|
|
224
|
+
nw, nh = next_obj_size
|
|
225
|
+
px, py = padding
|
|
226
|
+
sw, sh = self.screen_w, self.screen_h
|
|
227
|
+
x = ox - px - nw
|
|
228
|
+
y = oy + py
|
|
229
|
+
if x < 0 or y > sh - nh or y < 0:
|
|
230
|
+
raise ValueError('Out of screen')
|
|
231
|
+
return x, y
|
Kernel/KernelRun.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
import concurrent.futures
|
|
5
|
+
import pygame
|
|
6
|
+
|
|
7
|
+
from Kernel import PygameRender, MainScreen, EventDispatcher
|
|
8
|
+
|
|
9
|
+
quitnow = pygame.QUIT
|
|
10
|
+
class BreakThread(Exception):
|
|
11
|
+
def __init__(self, message):
|
|
12
|
+
super().__init__(message)
|
|
13
|
+
class Thread:
|
|
14
|
+
def __init__(self, screen: MainScreen, functions: list,
|
|
15
|
+
quitcondition: int | Callable[..., bool] | None = pygame.QUIT, fps: int| float=60):
|
|
16
|
+
self.functions = functions
|
|
17
|
+
self.quitcondition = quitcondition
|
|
18
|
+
self.running = True
|
|
19
|
+
self.fps = fps
|
|
20
|
+
self.clock = pygame.time.Clock()
|
|
21
|
+
self.break_requested = False
|
|
22
|
+
self.event_manager = EventDispatcher(screen)
|
|
23
|
+
self.dt = 1.0 / fps if fps > 0 else 0.016
|
|
24
|
+
def _loop_start(self):
|
|
25
|
+
while self.running and not self.break_requested:
|
|
26
|
+
self.dt = self.clock.tick(self.fps) / 1000.0
|
|
27
|
+
if self.quitcondition == pygame.QUIT:
|
|
28
|
+
self.running = self.event_manager.event_passdown()
|
|
29
|
+
if callable(self.quitcondition) and self.quitcondition():
|
|
30
|
+
self.running = False
|
|
31
|
+
for func in self.functions:
|
|
32
|
+
func()
|
|
33
|
+
def threadstart(self):
|
|
34
|
+
if self.quitcondition is not None or self.fps > 0:
|
|
35
|
+
try:
|
|
36
|
+
self._loop_start()
|
|
37
|
+
except BreakThread:
|
|
38
|
+
self.running = False
|
|
39
|
+
else:
|
|
40
|
+
for func in self.functions:
|
|
41
|
+
func()
|
|
42
|
+
self.running = False
|
|
43
|
+
def threadbreak(self):
|
|
44
|
+
self.break_requested = True
|
|
45
|
+
def immediate_break(self):
|
|
46
|
+
self.running = False
|
|
47
|
+
def run_parallel(self, cpu_func, *args):
|
|
48
|
+
with concurrent.futures.ProcessPoolExecutor() as executor:
|
|
49
|
+
future = executor.submit(cpu_func, *args)
|
|
50
|
+
return future
|
|
51
|
+
def check_future(self, future):
|
|
52
|
+
if future.done():
|
|
53
|
+
return future.result()
|
|
54
|
+
return None
|
|
55
|
+
def get_event(self):
|
|
56
|
+
return self.event_manager.event
|
|
57
|
+
class MainApplication(Thread):
|
|
58
|
+
def __init__(self, screen_size, screen_flags=0, screen_bg = (0,0,0),
|
|
59
|
+
fixed = False, quitcondition: int | Callable[..., bool] | None = quitnow,
|
|
60
|
+
fps: int | float=60, render_engine = PygameRender, caption = None, functions = None):
|
|
61
|
+
user_functions = functions if functions is not None else []
|
|
62
|
+
screen = MainScreen(screen_size, screen_flags, screen_bg, fixed)
|
|
63
|
+
screen.set_common_engine(render_engine)
|
|
64
|
+
if caption:
|
|
65
|
+
screen.set_caption(caption)
|
|
66
|
+
super().__init__(screen, user_functions, quitcondition, fps)
|
|
67
|
+
self.screen = screen
|
|
68
|
+
self.functions.append(self._main_render)
|
|
69
|
+
def _main_render(self):
|
|
70
|
+
for widget in self.screen.child.values():
|
|
71
|
+
widget.render()
|
|
72
|
+
if widget.child:
|
|
73
|
+
self._recursive_render(widget)
|
|
74
|
+
pygame.display.flip()
|
|
75
|
+
def add_action(self, func):
|
|
76
|
+
self.functions.append(func)
|
|
77
|
+
def _recursive_render(self, parent_widget):
|
|
78
|
+
for widget in parent_widget.child.values():
|
|
79
|
+
widget.render()
|
|
80
|
+
if widget.child:
|
|
81
|
+
self._recursive_render(widget)
|