'+ title + '
' + summary +'
├── mkdocs.yml
├── pygame_widgets
├── animations
│ ├── __init__.py
│ └── animation.py
├── __init__.py
├── exceptions.py
├── util.py
├── toggle.py
├── progressbar.py
├── mouse.py
├── popup.py
├── slider.py
├── widget.py
├── combobox.py
└── selection.py
├── site
├── img
│ ├── grid.png
│ └── favicon.ico
├── sitemap.xml.gz
├── images
│ ├── combobox.gif
│ ├── dropdown.gif
│ ├── slider.png
│ └── textbox.png
├── fonts
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── sitemap.xml
├── search
│ ├── main.js
│ └── worker.js
├── 404.html
├── css
│ └── base.css
├── js
│ └── base.js
├── common
│ └── index.html
├── animations
│ └── index.html
├── toggle
│ └── index.html
├── progressbar
│ └── index.html
├── index.html
├── slider
│ └── index.html
└── textbox
│ └── index.html
├── docs
├── images
│ ├── combobox.gif
│ ├── dropdown.gif
│ ├── slider.png
│ └── textbox.png
├── widgets
│ ├── common.md
│ ├── toggle.md
│ ├── progressbar.md
│ ├── slider.md
│ ├── textbox.md
│ ├── buttonarray.md
│ ├── dropdown.md
│ ├── combobox.md
│ └── button.md
├── animations
│ └── animations.md
├── CONTRIBUTING.md
└── index.md
├── .gitattributes
├── .readthedocs.yml
├── .github
└── ISSUE_TEMPLATE
│ └── bug-report.md
├── LICENSE
├── examples
├── button_array_example.py
└── button_example.py
├── README.md
└── CODE_OF_CONDUCT.md
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Pygame Widgets
2 | theme:
3 | name: readthedocs
4 | docs_dir: docs
--------------------------------------------------------------------------------
/pygame_widgets/animations/__init__.py:
--------------------------------------------------------------------------------
1 | from animation import Resize, Recolour, Translate
2 |
--------------------------------------------------------------------------------
/site/img/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/img/grid.png
--------------------------------------------------------------------------------
/site/sitemap.xml.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/sitemap.xml.gz
--------------------------------------------------------------------------------
/site/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/img/favicon.ico
--------------------------------------------------------------------------------
/docs/images/combobox.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/docs/images/combobox.gif
--------------------------------------------------------------------------------
/docs/images/dropdown.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/docs/images/dropdown.gif
--------------------------------------------------------------------------------
/docs/images/slider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/docs/images/slider.png
--------------------------------------------------------------------------------
/docs/images/textbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/docs/images/textbox.png
--------------------------------------------------------------------------------
/site/images/combobox.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/images/combobox.gif
--------------------------------------------------------------------------------
/site/images/dropdown.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/images/dropdown.gif
--------------------------------------------------------------------------------
/site/images/slider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/images/slider.png
--------------------------------------------------------------------------------
/site/images/textbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/images/textbox.png
--------------------------------------------------------------------------------
/site/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/site/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/site/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/site/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AustL/PygameWidgets/HEAD/site/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.css linguist-detectable=false
2 | *.java linguist-detectable=false
3 | *.py linguist-detectable=true
4 | *.js linguist-detectable=false
5 | *.html linguist-detectable=false
6 | *.xml linguist-detectable=false
--------------------------------------------------------------------------------
/pygame_widgets/__init__.py:
--------------------------------------------------------------------------------
1 | from pygame_widgets.mouse import Mouse
2 | from pygame_widgets.widget import WidgetHandler
3 |
4 | from pygame.event import Event
5 |
6 |
7 | def update(events: [Event]):
8 | Mouse.updateMouseState()
9 | WidgetHandler.main(events)
10 |
--------------------------------------------------------------------------------
/pygame_widgets/exceptions.py:
--------------------------------------------------------------------------------
1 | class InvalidParameter(Exception):
2 | """ Exception called if an invalid parameter is passed into an animation"""
3 |
4 |
5 | class InvalidParameterType(Exception):
6 | """ Exception called if an invalid parameter type is passed into an animation"""
7 |
--------------------------------------------------------------------------------
/docs/widgets/common.md:
--------------------------------------------------------------------------------
1 | # Common
2 |
3 | Functionality provided and required for all widgets.
4 |
5 | ## Mandatory Parameters
6 |
7 | _Note: Mandatory parameters must be supplied in order._
8 |
9 | | Parameter | Description | Type |
10 | | :---: | --- | :---: |
11 | | win | Surface to be displayed on. | pygame.Surface |
12 | | x | X-coordinate of top left. | int |
13 | | y | Y-coordinate of top left. | int |
14 | | width | Width of button in pixels. | int |
15 | | height | Height of button in pixels. | int |
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | # Build documentation with MkDocs
9 | #mkdocs:
10 | # configuration: mkdocs.yml
11 | mkdocs:
12 | configuration: mkdocs.yml
13 | fail_on_warning: true
14 | build:
15 | os: ubuntu-lts-latest
16 | tools:
17 | python: "3.10"
18 | # Optionally build your docs in additional formats such as PDF and ePub
19 | formats: all
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behaviour:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behaviour**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Version Numbers**
27 | - Pygame Widgets
28 | - Pygame
29 | - Python
30 |
--------------------------------------------------------------------------------
/pygame_widgets/util.py:
--------------------------------------------------------------------------------
1 | import pygame
2 |
3 |
4 | def drawText(win, text, colour, rect, font, align='centre'):
5 | rect = pygame.Rect(rect)
6 | y = rect.top
7 | lineSpacing = -2
8 |
9 | fontHeight = font.size('Tg')[1]
10 |
11 | while text:
12 | i = 1
13 |
14 | if y + fontHeight > rect.bottom:
15 | break
16 |
17 | while font.size(text[:i])[0] < rect.width and i < len(text):
18 | i += 1
19 |
20 | if i < len(text):
21 | i = text.rfind(' ', 0, i) + 1
22 |
23 | image: pygame.Surface = font.render(text[:i], 1, colour)
24 |
25 | imageRect: pygame.Rect = image.get_rect()
26 |
27 | imageRect.center = rect.center
28 |
29 | if align == 'left':
30 | imageRect.left = rect.left
31 | elif align == 'right':
32 | imageRect.right = rect.right
33 |
34 | win.blit(image, (imageRect.left, y))
35 | y += fontHeight + lineSpacing
36 |
37 | text = text[i:]
38 |
39 | return text
40 |
--------------------------------------------------------------------------------
/docs/animations/animations.md:
--------------------------------------------------------------------------------
1 | # Animations
2 |
3 | Create an animation by using the default Translate or Resize, inheriting from AnimationBase, or using AnimationBase
4 | directly.
5 |
6 | ## Example Usage
7 |
8 | ```Python
9 | import pygame_widgets
10 | import pygame
11 | from pygame_widgets.button import Button
12 | from pygame_widgets.animations import Resize
13 |
14 | pygame.init()
15 | win = pygame.display.set_mode((600, 600))
16 |
17 | button = Button(win, 100, 100, 300, 150)
18 |
19 | animation = Resize(button, 3, 200, 200)
20 | animation.start()
21 |
22 | run = True
23 | while run:
24 | events = pygame.event.get()
25 | for event in events:
26 | if event.type == pygame.QUIT:
27 | pygame.quit()
28 | run = False
29 | quit()
30 |
31 | win.fill((255, 255, 255))
32 |
33 | pygame_widgets.update(events)
34 | pygame.display.update()
35 | ```
36 |
37 | Over 3 seconds, the width of the button was changed from 300 to 200 and its height from 150 to 200. Since it is
38 | performed on a separate thread, the button is still able to function during the animation.
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 The Python Packaging Authority
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
--------------------------------------------------------------------------------
/examples/button_array_example.py:
--------------------------------------------------------------------------------
1 | import pygame
2 |
3 | import pygame_widgets
4 | from pygame_widgets.button import ButtonArray
5 |
6 | # Set up Pygame
7 | pygame.init()
8 | win = pygame.display.set_mode((600, 600))
9 |
10 | # Creates an array of buttons
11 | buttonArray = ButtonArray(
12 | # Mandatory Parameters
13 | win, # Surface to place button array on
14 | 50, # X-coordinate
15 | 50, # Y-coordinate
16 | 500, # Width
17 | 500, # Height
18 | (2, 2), # Shape: 2 buttons wide, 2 buttons tall
19 | border=100, # Distance between buttons and edge of array
20 | texts=('1', '2', '3', '4'), # Sets the texts of each button (counts left to right then top to bottom)
21 | # When clicked, print number
22 | onClicks=(lambda: print('1'), lambda: print('2'), lambda: print('3'), lambda: print('4'))
23 | )
24 |
25 | run = True
26 | while run:
27 | events = pygame.event.get()
28 | for event in events:
29 | if event.type == pygame.QUIT:
30 | pygame.quit()
31 | run = False
32 | quit()
33 |
34 | win.fill((255, 255, 255))
35 |
36 | pygame_widgets.update(events) # Call once every loop to allow widgets to render and listen
37 | pygame.display.update()
38 |
--------------------------------------------------------------------------------
/docs/widgets/toggle.md:
--------------------------------------------------------------------------------
1 | # Toggle
2 |
3 | Allows switching between true and false options
4 |
5 | ## Example Usage
6 |
7 | ```Python
8 | import pygame_widgets
9 | import pygame
10 | from pygame_widgets.toggle import Toggle
11 |
12 | pygame.init()
13 | win = pygame.display.set_mode((1000, 600))
14 |
15 | toggle = Toggle(win, 100, 100, 100, 40)
16 |
17 | run = True
18 | while run:
19 | events = pygame.event.get()
20 | for event in events:
21 | if event.type == pygame.QUIT:
22 | pygame.quit()
23 | run = False
24 | quit()
25 |
26 | win.fill((255, 255, 255))
27 |
28 | pygame_widgets.update(events)
29 | pygame.display.update()
30 | ```
31 |
32 | ## Optional Parameters
33 |
34 | | Parameter | Description | Type | Default |
35 | | :---: | --- | :---: | :---: |
36 | | startOn | Default value. | bool | False |
37 | | onColour | Colour of toggle when on. | (int, int, int) | (141, 185, 244) |
38 | | offColour | Colour of toggle when off. | (int, int, int) | (150, 150, 150) |
39 | | handleOnColour | Thickness of toggle handle when on. | (int, int, int) | (26, 115, 232) |
40 | | handleOffColour | Thickness of toggle handle when off. | (int, int, int) | (200, 200, 200) |
41 | | handleRadius | Radius of handle. | int | height / 1.3 |
--------------------------------------------------------------------------------
/examples/button_example.py:
--------------------------------------------------------------------------------
1 | import pygame
2 |
3 | import pygame_widgets
4 | from pygame_widgets.button import Button
5 |
6 | # Set up Pygame
7 | pygame.init()
8 | win = pygame.display.set_mode((600, 600))
9 |
10 | # Creates the button with optional parameters
11 | button = Button(
12 | # Mandatory Parameters
13 | win, # Surface to place button on
14 | 100, # X-coordinate of top left corner
15 | 100, # Y-coordinate of top left corner
16 | 300, # Width
17 | 150, # Height
18 |
19 | # Optional Parameters
20 | text='Hello', # Text to display
21 | fontSize=50, # Size of font
22 | margin=20, # Minimum distance between text/image and edge of button
23 | inactiveColour=(200, 50, 0), # Colour of button when not being interacted with
24 | hoverColour=(150, 0, 0), # Colour of button when being hovered over
25 | pressedColour=(0, 200, 20), # Colour of button when being clicked
26 | radius=20, # Radius of border corners (leave empty for not curved)
27 | onClick=lambda: print('Click') # Function to call when clicked on
28 | )
29 |
30 | run = True
31 | while run:
32 | events = pygame.event.get()
33 | for event in events:
34 | if event.type == pygame.QUIT:
35 | pygame.quit()
36 | run = False
37 | quit()
38 |
39 | win.fill((255, 255, 255))
40 |
41 | pygame_widgets.update(events) # Call once every loop to allow widgets to render and listen
42 | pygame.display.update()
43 |
--------------------------------------------------------------------------------
/docs/widgets/progressbar.md:
--------------------------------------------------------------------------------
1 | # Progress Bar
2 |
3 | Displays a continuously changing percentage
4 |
5 | ## Example Usage
6 |
7 | ```Python
8 | import pygame_widgets
9 | import pygame
10 | import time
11 | from pygame_widgets.progressbar import ProgressBar
12 |
13 | startTime = time.time()
14 |
15 | pygame.init()
16 | win = pygame.display.set_mode((1000, 600))
17 |
18 | progressBar = ProgressBar(win, 100, 100, 500, 40, lambda: 1 - (time.time() - startTime) / 10, curved=True)
19 |
20 | run = True
21 | while run:
22 | events = pygame.event.get()
23 | for event in events:
24 | if event.type == pygame.QUIT:
25 | pygame.quit()
26 | run = False
27 | quit()
28 |
29 | win.fill((255, 255, 255))
30 |
31 | pygame_widgets.update(events)
32 | pygame.display.update()
33 | ```
34 |
35 | This progress bar uses time to fill up, however, the progress function can be replaced by
36 | any other function call that provides a percentage.
37 |
38 |
39 | ## Mandatory Parameters
40 |
41 | | Parameter | Description | Type |
42 | | :---: | --- | :---: |
43 | | progress | Function that defines the percentage of the bar filled. | function -> float |
44 |
45 | ## Optional Parameters
46 |
47 | | Parameter | Description | Type | Default |
48 | | :---: | --- | :---: | :---: |
49 | | curved | Adds curved ends to the progress bar. | bool | False |
50 | | completedColour | Colour of completed section of progress bar. | (int, int, int) | (0, 200, 0) |
51 | | incompletedColour | Colour of incompleted section of progress bar. | (int, int, int) | (100, 100, 100) |
52 |
--------------------------------------------------------------------------------
/site/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 | ' + summary +''+ title + '
' + noResultsText + '
'); 45 | } 46 | } 47 | 48 | function doSearch () { 49 | var query = document.getElementById('mkdocs-search-query').value; 50 | if (query.length > min_search_length) { 51 | if (!window.Worker) { 52 | displayResults(search(query)); 53 | } else { 54 | searchWorker.postMessage({query: query}); 55 | } 56 | } else { 57 | // Clear results for short queries 58 | displayResults([]); 59 | } 60 | } 61 | 62 | function initSearch () { 63 | var search_input = document.getElementById('mkdocs-search-query'); 64 | if (search_input) { 65 | search_input.addEventListener("keyup", doSearch); 66 | } 67 | var term = getSearchTermFromLocation(); 68 | if (term) { 69 | search_input.value = term; 70 | doSearch(); 71 | } 72 | } 73 | 74 | function onWorkerMessage (e) { 75 | if (e.data.allowSearch) { 76 | initSearch(); 77 | } else if (e.data.results) { 78 | var results = e.data.results; 79 | displayResults(results); 80 | } else if (e.data.config) { 81 | min_search_length = e.data.config.min_search_length-1; 82 | } 83 | } 84 | 85 | if (!window.Worker) { 86 | console.log('Web Worker API not supported'); 87 | // load index in main thread 88 | $.getScript(joinUrl(base_url, "search/worker.js")).done(function () { 89 | console.log('Loaded worker'); 90 | init(); 91 | window.postMessage = function (msg) { 92 | onWorkerMessage({data: msg}); 93 | }; 94 | }).fail(function (jqxhr, settings, exception) { 95 | console.error('Could not load worker.js'); 96 | }); 97 | } else { 98 | // Wrap search in a web worker 99 | var searchWorker = new Worker(joinUrl(base_url, "search/worker.js")); 100 | searchWorker.postMessage({init: true}); 101 | searchWorker.onmessage = onWorkerMessage; 102 | } 103 | -------------------------------------------------------------------------------- /docs/widgets/dropdown.md: -------------------------------------------------------------------------------- 1 | # Dropdown 2 | 3 | A dropdown menu allowing the selection of various elements. 4 | 5 |  6 | 7 | ```Python 8 | import pygame_widgets 9 | import pygame 10 | from pygame_widgets.button import Button 11 | from pygame_widgets.dropdown import Dropdown 12 | 13 | pygame.init() 14 | win = pygame.display.set_mode((400, 280)) 15 | 16 | dropdown = Dropdown( 17 | win, 120, 10, 100, 50, name='Select Color', 18 | choices=[ 19 | 'Red', 20 | 'Blue', 21 | 'Yellow', 22 | ], 23 | borderRadius=3, colour=pygame.Color('green'), values=[1, 2, 'true'], direction='down', textHAlign='left' 24 | ) 25 | 26 | 27 | def print_value(): 28 | print(dropdown.getSelected()) 29 | 30 | 31 | button = Button( 32 | win, 10, 10, 100, 50, text='Print Value', fontSize=30, 33 | margin=20, inactiveColour=(255, 0, 0), pressedColour=(0, 255, 0), 34 | radius=5, onClick=print_value, font=pygame.font.SysFont('calibri', 10), 35 | textVAlign='bottom' 36 | ) 37 | 38 | run = True 39 | while run: 40 | events = pygame.event.get() 41 | for event in events: 42 | if event.type == pygame.QUIT: 43 | pygame.quit() 44 | run = False 45 | quit() 46 | 47 | win.fill((255, 255, 255)) 48 | 49 | pygame_widgets.update(events) 50 | pygame.display.update() 51 | ``` 52 | 53 | This is a classic dropdown, but with a twist: if you right-click on the top, it reset itself. To get the current value 54 | of the dropdown, we use the `getSelected()` methods. 55 | 56 | It returns: 57 | 58 | - `None` if nothing is selected 59 | - A string with the choice you selected if the optional arg `value` is not set 60 | - If the optional arg `value` is set, we return the value corresponding to the choice. 61 | 62 | For the example above: 63 | 64 | | Choice | Value | 65 | | :---: | :---: | 66 | | Red | 1 | 67 | | Blue | 2 | 68 | | Yellow | 3 | 69 | 70 | ## Mandatory Parameters 71 | 72 | _Note: Mandatory parameters must be supplied in order._ 73 | 74 | | Parameter | Description | Type | 75 | | :---: | --- | :---: | 76 | | name | Main name of the dropdown | str | 77 | | choices | Choices to display | list of str | 78 | 79 | ## Optional Parameters 80 | 81 | | Parameter | Description | Type | Default | 82 | | :---: | --- | :---: | :---: | 83 | | direction | Expansion direction. Can be 'down', 'up', 'left' or 'right'. | str | down | 84 | | values | optional return value corresponding to the choices. Must be the same length as `choices` |list|a copy of choices| 85 | | inactiveColour | Default colour when not pressed or hovered over. | (int, int, int) | (150, 150, 150) | 86 | | pressedColour | Colour when pressed. | (int, int, int) | (100, 100, 100) | 87 | | hoverColour | Colour when hovered over. | (int, int, int) | (125, 125, 125) | 88 | | onClick | Function to be called when clicked. | function | None | 89 | | onClickParams | Parameters to be fed into onClick function. | (*any) | () | 90 | | onRelease | Function to be called when released. | function | None | 91 | | onReleaseParams | Parameters to be fed into onRelease function. | (*any) | () | 92 | | textColour | Colour of text. | (int, int, int) | (0, 0, 0) | 93 | | fontSize | Size of text. | int | 20 | 94 | | font | Font of text. | pygame.font.Font | sans-serif | 95 | | textHAlign | Horizontal alignment of text. Can be 'centre', 'left' or 'right'. | str | 'centre' | 96 | | borderColour | Colour of border. | (int, int, int) | (0, 0, 0) | 97 | | borderThickness | Thickness of border. | int | 3 | 98 | | borderRadius | Border radius. Set to 0 for no radius. | int | 0 | 99 | -------------------------------------------------------------------------------- /pygame_widgets/mouse.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | import pygame 3 | import time 4 | 5 | 6 | class MouseState(Enum): 7 | HOVER = 0 8 | CLICK = 1 9 | RIGHT_CLICK = 2 10 | DRAG = 3 11 | RIGHT_DRAG = 4 # Not sure when this is ever used but added anyway for completeness 12 | RELEASE = 5 13 | RIGHT_RELEASE = 6 14 | 15 | 16 | class Mouse: 17 | _refreshTime = 0.01 18 | 19 | # Redundant currently, may use for double click handling 20 | lastLeftClick = 0 21 | lastRightClick = 0 22 | leftClickElapsedTime = 0 23 | rightClickElapsedTime = 0 24 | 25 | _mouseState = MouseState.HOVER 26 | 27 | @staticmethod 28 | def listen(): 29 | listening = True 30 | while listening: 31 | try: 32 | Mouse.updateMouseState() 33 | except pygame.error: 34 | listening = False 35 | time.sleep(Mouse._refreshTime) 36 | 37 | @staticmethod 38 | def updateMouseState(): 39 | leftPressed = pygame.mouse.get_pressed()[0] 40 | rightPressed = pygame.mouse.get_pressed()[2] 41 | 42 | if leftPressed: 43 | if Mouse._mouseState == MouseState.CLICK or Mouse._mouseState == MouseState.DRAG: 44 | Mouse._mouseState = MouseState.DRAG 45 | else: 46 | Mouse._mouseState = MouseState.CLICK 47 | 48 | elif rightPressed: 49 | if Mouse._mouseState == MouseState.RIGHT_CLICK or Mouse._mouseState == MouseState.RIGHT_DRAG: 50 | Mouse._mouseState = MouseState.RIGHT_DRAG 51 | else: 52 | Mouse._mouseState = MouseState.RIGHT_CLICK 53 | else: 54 | # If previously was held down, call the release 55 | if Mouse._mouseState == MouseState.CLICK or Mouse._mouseState == MouseState.DRAG: 56 | Mouse._mouseState = MouseState.RELEASE 57 | 58 | elif Mouse._mouseState == MouseState.RIGHT_CLICK or Mouse._mouseState == MouseState.RIGHT_DRAG: 59 | Mouse._mouseState = MouseState.RIGHT_RELEASE 60 | 61 | else: 62 | Mouse._mouseState = MouseState.HOVER 63 | 64 | @staticmethod 65 | def updateElapsedTime(): 66 | """Also redundant until double click functionality implemented""" 67 | if Mouse._mouseState == MouseState.CLICK or Mouse._mouseState == MouseState.DRAG: 68 | Mouse.leftClickElapsedTime = time.time() - Mouse.lastLeftClick 69 | elif Mouse._mouseState == MouseState.RIGHT_CLICK or Mouse._mouseState == MouseState.RIGHT_DRAG: 70 | Mouse.rightClickElapsedTime = time.time() - Mouse.lastRightClick 71 | 72 | @staticmethod 73 | def getMouseState() -> MouseState: 74 | return Mouse._mouseState 75 | 76 | @staticmethod 77 | def getMousePos() -> (int, int): 78 | return pygame.mouse.get_pos() 79 | 80 | @staticmethod 81 | def setRefreshRatePerSec(refreshRate): 82 | Mouse._refreshTime = 1 / refreshRate if refreshRate != 0 else 0 83 | 84 | 85 | if __name__ == '__main__': 86 | pygame.init() 87 | win = pygame.display.set_mode((600, 600)) 88 | 89 | run = True 90 | while run: 91 | events = pygame.event.get() 92 | for event in events: 93 | if event.type == pygame.QUIT: 94 | pygame.quit() 95 | run = False 96 | quit() 97 | 98 | win.fill((255, 255, 255)) 99 | 100 | Mouse.updateMouseState() 101 | 102 | pygame.display.update() 103 | time.sleep(0.1) 104 | -------------------------------------------------------------------------------- /docs/widgets/combobox.md: -------------------------------------------------------------------------------- 1 | # ComboBox 2 | 3 | A dropdown menu allowing the selection of various elements using a search bar. 4 | 5 | It is similar to `Dropdown` but includes a `TextBox` that allows searching of options. 6 | 7 | The parameters of the `TextBox` can be made different 8 | from the ones of the dropdown if they are specified in 9 | the `textboxKwargs` parameter. 10 | 11 |  12 | 13 | ```Python 14 | import pygame_widgets 15 | import pygame 16 | 17 | from pygame_widgets.combobox import ComboBox 18 | from pygame_widgets.button import Button 19 | 20 | pygame.init() 21 | win = pygame.display.set_mode((600, 600)) 22 | 23 | comboBox = ComboBox( 24 | win, 120, 10, 250, 50, name='Select Color', 25 | choices=pygame.colordict.THECOLORS.keys(), 26 | maxResults=4, 27 | font=pygame.font.SysFont('calibri', 30), 28 | borderRadius=3, colour=(0, 200, 50), direction='down', 29 | textHAlign='left' 30 | ) 31 | 32 | 33 | def output(): 34 | comboBox.textBar.colour = comboBox.getText() 35 | 36 | 37 | button = Button( 38 | win, 10, 10, 100, 50, text='Set Colour', fontSize=30, 39 | margin=15, inactiveColour=(200, 0, 100), pressedColour=(0, 255, 0), 40 | radius=5, onClick=output, font=pygame.font.SysFont('calibri', 18), 41 | textVAlign='bottom' 42 | ) 43 | 44 | run = True 45 | while run: 46 | events = pygame.event.get() 47 | for event in events: 48 | if event.type == pygame.QUIT: 49 | pygame.quit() 50 | run = False 51 | quit() 52 | 53 | win.fill((255, 255, 255)) 54 | 55 | pygame_widgets.update(events) 56 | pygame.display.update() 57 | ``` 58 | 59 | This is a classic combo box. The current selected text 60 | can be accessed through the `getText()` methods. 61 | 62 | It returns the current text in the search bar. 63 | 64 | 65 | ## Mandatory Parameters 66 | 67 | _Note: Mandatory parameters must be supplied in order._ 68 | 69 | | Parameter | Description | Type | 70 | | :---: | --- | :---: | 71 | | choices | Choices to appear in the list | list of str | 72 | 73 | ## Optional Parameters 74 | 75 | | Parameter | Description | Type | Default | 76 | | :---: | --- | :---: | :---: | 77 | | direction | Expansion direction. Can be 'down', 'up', 'left' or 'right'. | str | down | 78 | | inactiveColour | Default colour when not pressed or hovered over. | (int, int, int) | (150, 150, 150) | 79 | | pressedColour | Colour when pressed. | (int, int, int) | (100, 100, 100) | 80 | | hoverColour | Colour when hovered over. | (int, int, int) | (125, 125, 125) | 81 | | maxChoices | Maximum number of choices to display | int | len(choices) | 82 | | searchAlgo | Algorithm to be used to search through choices. | function(str, list) -> list | ComboBox._defaultSearch | 83 | | onSelected | Function to be called when a search choice is selected. | function | None | 84 | | onSelectedParams | Parameters to be fed into onSelected function. | (*any) | () | 85 | | onStartSearch | Function to be called when a search is started by user (clicking on the search box). | function | None | 86 | | onStartSearchParams | Parameters to be fed into onStartSearch function. | (*any) | () | 87 | | onStopSearch | Function to be called when a search is stopped (clicking outside the search dropdown, or selecting a choice). | function | None | 88 | | onStopSearchParams | Parameters to be fed into onStopSearch function. | (*any) | () | 89 | | textColour | Colour of text. | (int, int, int) | (0, 0, 0) | 90 | | fontSize | Size of text. | int | 20 | 91 | | font | Font of text. | pygame.font.Font | sans-serif | 92 | | textHAlign | Horizontal alignment of text. Can be 'centre', 'left' or 'right'. | str | 'centre' | 93 | | borderColour | Colour of border. | (int, int, int) | (0, 0, 0) | 94 | | borderThickness | Thickness of border. | int | 3 | 95 | | borderRadius | Border radius. Set to 0 for no radius. | int | 0 | 96 | | textboxKwargs | Optional different parameters only for the `TextBox`. | dict | {} | 97 | -------------------------------------------------------------------------------- /site/search/worker.js: -------------------------------------------------------------------------------- 1 | var base_path = 'function' === typeof importScripts ? '.' : '/search/'; 2 | var allowSearch = false; 3 | var index; 4 | var documents = {}; 5 | var lang = ['en']; 6 | var data; 7 | 8 | function getScript(script, callback) { 9 | console.log('Loading script: ' + script); 10 | $.getScript(base_path + script).done(function () { 11 | callback(); 12 | }).fail(function (jqxhr, settings, exception) { 13 | console.log('Error: ' + exception); 14 | }); 15 | } 16 | 17 | function getScriptsInOrder(scripts, callback) { 18 | if (scripts.length === 0) { 19 | callback(); 20 | return; 21 | } 22 | getScript(scripts[0], function() { 23 | getScriptsInOrder(scripts.slice(1), callback); 24 | }); 25 | } 26 | 27 | function loadScripts(urls, callback) { 28 | if( 'function' === typeof importScripts ) { 29 | importScripts.apply(null, urls); 30 | callback(); 31 | } else { 32 | getScriptsInOrder(urls, callback); 33 | } 34 | } 35 | 36 | function onJSONLoaded () { 37 | data = JSON.parse(this.responseText); 38 | var scriptsToLoad = ['lunr.js']; 39 | if (data.config && data.config.lang && data.config.lang.length) { 40 | lang = data.config.lang; 41 | } 42 | if (lang.length > 1 || lang[0] !== "en") { 43 | scriptsToLoad.push('lunr.stemmer.support.js'); 44 | if (lang.length > 1) { 45 | scriptsToLoad.push('lunr.multi.js'); 46 | } 47 | if (lang.includes("ja") || lang.includes("jp")) { 48 | scriptsToLoad.push('tinyseg.js'); 49 | } 50 | for (var i=0; i < lang.length; i++) { 51 | if (lang[i] != 'en') { 52 | scriptsToLoad.push(['lunr', lang[i], 'js'].join('.')); 53 | } 54 | } 55 | } 56 | loadScripts(scriptsToLoad, onScriptsLoaded); 57 | } 58 | 59 | function onScriptsLoaded () { 60 | console.log('All search scripts loaded, building Lunr index...'); 61 | if (data.config && data.config.separator && data.config.separator.length) { 62 | lunr.tokenizer.separator = new RegExp(data.config.separator); 63 | } 64 | 65 | if (data.index) { 66 | index = lunr.Index.load(data.index); 67 | data.docs.forEach(function (doc) { 68 | documents[doc.location] = doc; 69 | }); 70 | console.log('Lunr pre-built index loaded, search ready'); 71 | } else { 72 | index = lunr(function () { 73 | if (lang.length === 1 && lang[0] !== "en" && lunr[lang[0]]) { 74 | this.use(lunr[lang[0]]); 75 | } else if (lang.length > 1) { 76 | this.use(lunr.multiLanguage.apply(null, lang)); // spread operator not supported in all browsers: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Browser_compatibility 77 | } 78 | this.field('title'); 79 | this.field('text'); 80 | this.ref('location'); 81 | 82 | for (var i=0; i < data.docs.length; i++) { 83 | var doc = data.docs[i]; 84 | this.add(doc); 85 | documents[doc.location] = doc; 86 | } 87 | }); 88 | console.log('Lunr index built, search ready'); 89 | } 90 | allowSearch = true; 91 | postMessage({config: data.config}); 92 | postMessage({allowSearch: allowSearch}); 93 | } 94 | 95 | function init () { 96 | var oReq = new XMLHttpRequest(); 97 | oReq.addEventListener("load", onJSONLoaded); 98 | var index_path = base_path + '/search_index.json'; 99 | if( 'function' === typeof importScripts ){ 100 | index_path = 'search_index.json'; 101 | } 102 | oReq.open("GET", index_path); 103 | oReq.send(); 104 | } 105 | 106 | function search (query) { 107 | if (!allowSearch) { 108 | console.error('Assets for search still loading'); 109 | return; 110 | } 111 | 112 | var resultDocuments = []; 113 | var results = index.search(query); 114 | for (var i=0; i < results.length; i++){ 115 | var result = results[i]; 116 | doc = documents[result.ref]; 117 | doc.summary = doc.text.substring(0, 200); 118 | resultDocuments.push(doc); 119 | } 120 | return resultDocuments; 121 | } 122 | 123 | if( 'function' === typeof importScripts ) { 124 | onmessage = function (e) { 125 | if (e.data.init) { 126 | init(); 127 | } else if (e.data.query) { 128 | postMessage({ results: search(e.data.query) }); 129 | } else { 130 | console.error("Worker - Unrecognized message: " + e); 131 | } 132 | }; 133 | } 134 | -------------------------------------------------------------------------------- /pygame_widgets/animations/animation.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | import time 3 | import pygame 4 | 5 | import pygame_widgets 6 | from pygame_widgets.exceptions import InvalidParameter, InvalidParameterType 7 | 8 | 9 | class AnimationBase: 10 | def __init__(self, widget, timeout, allowMultiple=False, **kwargs): 11 | """Base for animations 12 | 13 | :param widget: The widget that the animation targets 14 | :param timeout: The time of the animation in seconds 15 | :param kwargs: The target of the animation, e.g. x=10 changes x position to 10 16 | """ 17 | self.widget = widget 18 | self.timeout = timeout 19 | self.allowMultiple = allowMultiple 20 | self.params = kwargs 21 | self.thread = Thread() 22 | 23 | self.started = False 24 | self.runOnce = False 25 | 26 | self.checkValidParams() 27 | 28 | def checkValidParams(self): 29 | for param, target in self.params.items(): 30 | value = self.widget.get(param) 31 | if value is None: 32 | raise InvalidParameter( 33 | f'The parameter <{param}> is not a valid attribute of type {type(self.widget)}' 34 | ) 35 | elif type(value) != type(target): 36 | raise InvalidParameterType( 37 | f'Expected parameter <{param}> to be of type {type(value)} but found type {type(target)}' 38 | ) 39 | 40 | def start(self): 41 | if not self.started and not (self.runOnce and not self.allowMultiple): 42 | self.thread = Thread(target=self.loop) 43 | self.thread.start() 44 | 45 | def loop(self): 46 | self.started = self.runOnce = True 47 | 48 | start = time.time() 49 | 50 | initialNumberParams = {} 51 | initialTupleParams = {} 52 | for param, target in self.params.items(): 53 | initialValue = self.widget.get(param) 54 | if isinstance(initialValue, (int, float)): 55 | initialNumberParams[param] = initialValue 56 | elif isinstance(initialValue, (tuple, list)): 57 | initialTupleParams[param] = tuple(initialValue) 58 | 59 | # Animate 60 | while time.time() - start < self.timeout: 61 | step = (time.time() - start) / self.timeout 62 | 63 | # Numeric animation 64 | for param, initialValue in initialNumberParams.items(): 65 | target = self.params[param] 66 | newValue = initialValue + step * (target - initialValue) 67 | self.widget.set(param, newValue) 68 | 69 | # Tuple animation 70 | for param, initialTuple in initialTupleParams.items(): 71 | target = self.params[param] 72 | newValue = tuple( 73 | initialTuple[i] + step * (target[i] - initialTuple[i]) for i in range(len(initialTuple))) 74 | self.widget.set(param, newValue) 75 | 76 | # Ensure value is exactly correct at end 77 | for param, target in self.params.items(): 78 | self.widget.set(param, target) 79 | 80 | self.started = False 81 | 82 | 83 | class Translate(AnimationBase): 84 | def __init__(self, widget, timeout, x, y): 85 | super().__init__(widget, timeout, x=x, y=y) 86 | 87 | 88 | class Resize(AnimationBase): 89 | def __init__(self, widget, timeout, width, height): 90 | super().__init__(widget, timeout, width=width, height=height) 91 | 92 | 93 | class Recolour(AnimationBase): 94 | def __init__(self, widget, timeout, colour): 95 | super().__init__(widget, timeout, colour=colour) 96 | 97 | 98 | if __name__ == '__main__': 99 | from pygame_widgets.button import Button 100 | 101 | 102 | def animate(): 103 | resize.start() 104 | translate.start() 105 | 106 | 107 | pygame.init() 108 | win = pygame.display.set_mode((600, 600)) 109 | 110 | button = Button(win, 100, 100, 300, 150, text="Hello", inactiveColour=(0, 200, 0), hoverColour=(0, 200, 0)) 111 | 112 | resize = Resize(button, 3, 200, 200) 113 | translate = Recolour(button, 5, (0, 100, 100)) 114 | button.setOnClick(animate) 115 | 116 | run = True 117 | while run: 118 | events = pygame.event.get() 119 | for event in events: 120 | if event.type == pygame.QUIT: 121 | pygame.quit() 122 | run = False 123 | quit() 124 | 125 | win.fill((255, 255, 255)) 126 | 127 | pygame_widgets.update(events) 128 | pygame.display.update() 129 | -------------------------------------------------------------------------------- /pygame_widgets/popup.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import tkinter as tk 3 | from tkinter import messagebox 4 | from enum import Enum 5 | 6 | import pygame_widgets 7 | from pygame_widgets.widget import WidgetBase 8 | 9 | tk.Tk().wm_withdraw() 10 | 11 | 12 | class PopupType(Enum): 13 | INFO = 0 14 | ERROR = 1 15 | WARNING = 2 16 | QUESTION = 3 17 | OK_CANCEL = 4 18 | YES_NO = 5 19 | YES_NO_CANCEL = 6 20 | RETRY_CANCEL = 7 21 | 22 | 23 | class Popup(WidgetBase): 24 | def __init__(self, win: pygame.Surface, x: int, y: int, width: int, height: int, popupType: PopupType, 25 | title: str, text: str, trigger=lambda *args: None, *buttons, **kwargs): 26 | super().__init__(win, x, y, width, height) 27 | self.popupType = popupType 28 | self.title = title 29 | self.text = text 30 | self.trigger = trigger 31 | self.buttons = buttons 32 | 33 | self.margin = kwargs.get('margin', 20) 34 | 35 | self.titleColour = kwargs.get('titleColour', (0, 0, 0)) 36 | self.titleSize = kwargs.get('titleSize', 40) 37 | self.titleFont = kwargs.get('titleFont', pygame.font.SysFont('calibri', self.titleSize, True)) 38 | self.titleRect = self.alignTitleRect() 39 | 40 | self.textColour = kwargs.get('textColour', (0, 0, 0)) 41 | self.textSize = kwargs.get('textSize', 18) 42 | self.textFont = kwargs.get('textFont', pygame.font.SysFont('calibri', self.textSize)) 43 | self.textRect = self.alignTextRect() 44 | 45 | self.radius = kwargs.get('radius', 0) 46 | 47 | self.colour = kwargs.get('colour', (150, 150, 150)) 48 | self.shadowDistance = kwargs.get('shadowDistance', 0) 49 | self.shadowColour = kwargs.get('shadowColour', (210, 210, 180)) 50 | 51 | self.result = None 52 | 53 | self.hide() 54 | 55 | def alignTitleRect(self): 56 | return pygame.Rect(self._x + self.margin, self._y + self.margin, 57 | self._width - self.margin * 2, self._height // 3 - self.margin * 2) 58 | 59 | def alignTextRect(self): 60 | return pygame.Rect(self._x + self.margin, self._y + self._height // 3, 61 | self._width - self.margin * 2, self._height // 2 - self.margin * 2) 62 | 63 | def listen(self, events): 64 | if self.trigger(): 65 | self.show() 66 | messagebox.showinfo(self.title, self.text) 67 | 68 | def draw(self): 69 | pass 70 | 71 | def show(self): 72 | super().show() 73 | match self.popupType: 74 | case PopupType.INFO: 75 | messagebox.showinfo(self.title, self.text) 76 | case PopupType.ERROR: 77 | messagebox.showerror(self.title, self.text) 78 | case PopupType.WARNING: 79 | messagebox.showwarning(self.title, self.text) 80 | case PopupType.QUESTION: 81 | self.result = messagebox.askquestion(self.title, self.text) 82 | case PopupType.OK_CANCEL: 83 | self.result = messagebox.askokcancel(self.title, self.text) 84 | case PopupType.YES_NO: 85 | self.result = messagebox.askyesno(self.title, self.text) 86 | case PopupType.YES_NO_CANCEL: 87 | self.result = messagebox.askyesnocancel(self.title, self.text) 88 | case PopupType.RETRY_CANCEL: 89 | self.result = messagebox.askretrycancel(self.title, self.text) 90 | 91 | def getResult(self): 92 | return self.result 93 | 94 | 95 | if __name__ == '__main__': 96 | from pygame_widgets.button import Button 97 | 98 | def setButtonColour(): 99 | if popup.getResult(): 100 | button.setInactiveColour('green') 101 | elif popup.getResult() == False: 102 | button.setInactiveColour('red') 103 | 104 | pygame.init() 105 | win = pygame.display.set_mode((600, 600)) 106 | 107 | popup = Popup(win, 100, 100, 400, 400, PopupType.YES_NO, 'Popup', 108 | 'This is the text in the popup. Would you like to continue? The buttons below can be customised.', 109 | radius=20, textSize=20) 110 | 111 | button = Button(win, 100, 100, 400, 400, text='Popup', onClick=popup.show) 112 | 113 | run = True 114 | while run: 115 | events = pygame.event.get() 116 | for event in events: 117 | if event.type == pygame.QUIT: 118 | pygame.quit() 119 | run = False 120 | quit() 121 | 122 | win.fill((255, 255, 255)) 123 | 124 | pygame_widgets.update(events) 125 | pygame.display.update() 126 | setButtonColour() 127 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /docs/widgets/button.md: -------------------------------------------------------------------------------- 1 | # Button 2 | 3 | A button that allows fully customisable text, images, colours and functions. 4 | 5 | ## Example Usage 6 | 7 | Note: See example 8 | 9 | ```Python 10 | import pygame 11 | 12 | import pygame_widgets 13 | from pygame_widgets.button import Button 14 | 15 | # Set up Pygame 16 | pygame.init() 17 | win = pygame.display.set_mode((600, 600)) 18 | 19 | # Creates the button with optional parameters 20 | button = Button( 21 | # Mandatory Parameters 22 | win, # Surface to place button on 23 | 100, # X-coordinate of top left corner 24 | 100, # Y-coordinate of top left corner 25 | 300, # Width 26 | 150, # Height 27 | 28 | # Optional Parameters 29 | text='Hello', # Text to display 30 | fontSize=50, # Size of font 31 | margin=20, # Minimum distance between text/image and edge of button 32 | inactiveColour=(200, 50, 0), # Colour of button when not being interacted with 33 | hoverColour=(150, 0, 0), # Colour of button when being hovered over 34 | pressedColour=(0, 200, 20), # Colour of button when being clicked 35 | radius=20, # Radius of border corners (leave empty for not curved) 36 | onClick=lambda: print('Click') # Function to call when clicked on 37 | ) 38 | 39 | run = True 40 | while run: 41 | events = pygame.event.get() 42 | for event in events: 43 | if event.type == pygame.QUIT: 44 | pygame.quit() 45 | run = False 46 | quit() 47 | 48 | win.fill((255, 255, 255)) 49 | 50 | pygame_widgets.update(events) # Call once every loop to allow widgets to render and listen 51 | pygame.display.update() 52 | ``` 53 | 54 | This button will be placed at (100, 100) with a width of 300 and a height of 150, display the text 'Hello' with font 55 | size 50, leaving a margin of 20 and a radius of 20. When hovered over, the button changes to a darker red. 56 | When clicked, the button will change colour from red to green and 'Click' will be printed to the console. 57 | 58 | ## Optional Parameters 59 | 60 | | Parameter | Description | Type | Default | 61 | |:--------------------:|----------------------------------------------------------------------------------|:----------------:|:---------------:| 62 | | inactiveColour | Default colour when not pressed or hovered over. | (int, int, int) | (150, 150, 150) | 63 | | pressedColour | Colour when pressed. | (int, int, int) | (100, 100, 100) | 64 | | hoverColour | Colour when hovered over. | (int, int, int) | (125, 125, 125) | 65 | | shadowDistance | Distance to projected shadow. Set to 0 if no shadow desired. | int | 0 | 66 | | shadowColour | Colour of shadow | (int, int, int) | (210, 210, 180) | 67 | | onClick | Function to be called when clicked. | function | None | 68 | | onClickParams | Parameters to be fed into onClick function. | (*any) | () | 69 | | onRelease | Function to be called when released. | function | None | 70 | | onReleaseParams | Parameters to be fed into onRelease function. | (*any) | () | 71 | | onHover | Function to be continuously called when the mouse hovers over the button. | function | None | 72 | | onHoverParams | Parameters to be fed into onHover function. | (*any) | () | 73 | | onHoverRelease | Function to be called once when the mouse stops hovering over the button. | function | None | 74 | | onHoverReleaseParams | Parameters to be fed into onHoverRelease function. | (*any) | () | 75 | | textColour | Colour of text. | (int, int, int) | (0, 0, 0) | 76 | | fontSize | Size of text. | int | 20 | 77 | | text | String to be displayed. | str | '' | 78 | | font | Font of text. | pygame.font.Font | Calibri | 79 | | textHAlign | Horizontal alignment of text. Can be 'centre', 'left' or 'right'. | str | 'centre' | 80 | | textVAlign | Vertical alignment of text. Can be 'centre', 'top' or 'bottom'. | str | 'centre' | 81 | | margin | Minimum distance between text / image and edge. | int | 20 | 82 | | image | Image to be displayed. | pygame.Surface | None | 83 | | imageHAlign | Horizontal alignment of image. Can be 'centre', 'left' or 'right'. | str | 'centre' | 84 | | imageVAlign | Vertical alignment of image. Can be 'centre', 'top' or 'bottom'. | str | 'centre' | 85 | | radius | Border radius. Set to half of width for circular button. Set to 0 for no radius. | int | 0 | 86 | -------------------------------------------------------------------------------- /pygame_widgets/slider.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame import gfxdraw 3 | import math 4 | 5 | import pygame_widgets 6 | from pygame_widgets.widget import WidgetBase 7 | from pygame_widgets.mouse import Mouse, MouseState 8 | 9 | 10 | class Slider(WidgetBase): 11 | def __init__(self, win, x, y, width, height, **kwargs): 12 | super().__init__(win, x, y, width, height) 13 | 14 | self.selected = False 15 | self.valueColour = kwargs.get('valueColour', (0, 35, 255)) 16 | 17 | self.min = kwargs.get('min', 0) 18 | self.max = kwargs.get('max', 99) 19 | self.step = kwargs.get('step', 1) 20 | 21 | self.colour = kwargs.get('colour', (200, 200, 200)) 22 | self.handleColour = kwargs.get('handleColour', (0, 0, 0)) 23 | 24 | self.borderThickness = kwargs.get('borderThickness', 3) 25 | self.borderColour = kwargs.get('borderColour', (0, 0, 0)) 26 | 27 | self.value = self.round(kwargs.get('initial', (self.max + self.min) / 2)) 28 | self.value = max(min(self.value, self.max), self.min) 29 | 30 | self.curved = kwargs.get('curved', True) 31 | 32 | self.vertical = kwargs.get('vertical', False) 33 | 34 | self.draggableAnywhere = kwargs.get('draggableAnywhere', True) 35 | 36 | if self.curved: 37 | if self.vertical: 38 | self.radius = self._width // 2 39 | else: 40 | self.radius = self._height // 2 41 | 42 | if self.vertical: 43 | self.handleRadius = kwargs.get('handleRadius', int(self._width / 1.3)) 44 | else: 45 | self.handleRadius = kwargs.get('handleRadius', int(self._height / 1.3)) 46 | 47 | def listen(self, events): 48 | if not self._hidden and not self._disabled: 49 | mouseState = Mouse.getMouseState() 50 | x, y = Mouse.getMousePos() 51 | 52 | if self.contains(x, y): 53 | if mouseState == MouseState.CLICK: 54 | self.selected = True 55 | 56 | if mouseState == MouseState.RELEASE: 57 | self.selected = False 58 | 59 | if self.selected: 60 | if self.vertical: 61 | self.value = self.max - self.round((y - self._y) / self._height * (self.max - self.min)) 62 | self.value = max(min(self.value, self.max), self.min) 63 | else: 64 | self.value = self.round((x - self._x) / self._width * (self.max - self.min) + self.min) 65 | self.value = max(min(self.value, self.max), self.min) 66 | 67 | def draw(self): 68 | if not self._hidden: 69 | pygame.draw.rect(self.win, self.colour, (self._x, self._y, self._width, self._height)) 70 | 71 | if self.vertical: 72 | if self.curved: 73 | pygame.draw.circle(self.win, self.colour, (self._x + self._width // 2, self._y), self.radius) 74 | pygame.draw.circle(self.win, self.colour, (self._x + self._width // 2, self._y + self._height), 75 | self.radius) 76 | circle = (self._x + self._width // 2, 77 | int(self._y + (self.max - self.value) / (self.max - self.min) * self._height)) 78 | 79 | pygame.draw.circle(self.win, self.valueColour, (self._x + self._width // 2, self._y + self._height), 80 | self.radius) 81 | pygame.draw.rect(self.win, self.valueColour, 82 | (self._x, self._y + int(self._height * (1 - self.value/self.max)), self._width, int(self._height * self.value/self.max))) 83 | 84 | else: 85 | if self.curved: 86 | pygame.draw.circle(self.win, self.colour, (self._x, self._y + self._height // 2), self.radius) 87 | pygame.draw.circle(self.win, self.colour, (self._x + self._width, self._y + self._height // 2), 88 | self.radius) 89 | circle = (int(self._x + (self.value - self.min) / (self.max - self.min) * self._width), 90 | self._y + self._height // 2) 91 | pygame.draw.circle(self.win, self.valueColour, (self._x, self._y + self._height // 2), 92 | self.radius) 93 | pygame.draw.rect(self.win, self.valueColour, 94 | (self._x, self._y, int(self._width * self.value/self.max)-self.radius, self._height)) 95 | 96 | gfxdraw.filled_circle(self.win, *circle, self.handleRadius, self.handleColour) 97 | gfxdraw.aacircle(self.win, *circle, self.handleRadius, self.handleColour) 98 | 99 | def contains(self, x, y): 100 | if self.draggableAnywhere: 101 | return pygame.rect.Rect(self._x, self._y, self._width, self._height).collidepoint(x, y) 102 | 103 | else: 104 | if self.vertical: 105 | handleX = self._x + self._width // 2 106 | handleY = int(self._y + (self.max - self.value) / (self.max - self.min) * self._height) 107 | else: 108 | handleX = int(self._x + (self.value - self.min) / (self.max - self.min) * self._width) 109 | handleY = self._y + self._height // 2 110 | 111 | if math.sqrt((handleX - x) ** 2 + (handleY - y) ** 2) <= self.handleRadius: 112 | return True 113 | 114 | return False 115 | 116 | 117 | def round(self, value): 118 | return self.step * round(value / self.step) 119 | 120 | def getValue(self): 121 | return self.value 122 | 123 | def setValue(self, value): 124 | self.value = value 125 | 126 | 127 | if __name__ == '__main__': 128 | from pygame_widgets.textbox import TextBox 129 | 130 | pygame.init() 131 | win = pygame.display.set_mode((1000, 600)) 132 | 133 | slider = Slider(win, 100, 100, 800, 40, min=0, max=99, step=1) 134 | output = TextBox(win, 475, 200, 50, 50, fontSize=30) 135 | 136 | v_slider = Slider(win, 900, 200, 40, 300, min=0, max=99, step=1, vertical=True) 137 | v_output = TextBox(win, 800, 320, 50, 50, fontSize=30) 138 | 139 | output.disable() 140 | v_output.disable() 141 | 142 | run = True 143 | while run: 144 | events = pygame.event.get() 145 | for event in events: 146 | if event.type == pygame.QUIT: 147 | pygame.quit() 148 | run = False 149 | quit() 150 | 151 | win.fill((255, 255, 255)) 152 | 153 | output.setText(slider.getValue()) 154 | v_output.setText(v_slider.getValue()) 155 | 156 | pygame_widgets.update(events) 157 | pygame.display.update() 158 | -------------------------------------------------------------------------------- /pygame_widgets/widget.py: -------------------------------------------------------------------------------- 1 | import weakref 2 | 3 | from collections.abc import MutableSet 4 | from collections import OrderedDict 5 | 6 | from abc import abstractmethod, ABC 7 | 8 | from pygame.event import Event 9 | 10 | from pygame_widgets.mouse import Mouse 11 | 12 | 13 | # Implementation of an insertion-ordered set. Necessary to keep track of the order in which widgets are added. 14 | class OrderedSet(MutableSet): 15 | def __init__(self, values=()): 16 | self._od = OrderedDict().fromkeys(values) 17 | 18 | def __len__(self): 19 | return len(self._od) 20 | 21 | def __iter__(self): 22 | return iter(self._od) 23 | 24 | def __contains__(self, value): 25 | return value in self._od 26 | 27 | def add(self, value): 28 | self._od[value] = None 29 | 30 | def discard(self, value): 31 | self._od.pop(value, None) 32 | 33 | def move_to_end(self, value): 34 | self._od.move_to_end(value) 35 | 36 | def move_to_start(self, value): 37 | self._od.move_to_end(value, last=False) 38 | 39 | 40 | 41 | class OrderedWeakset(weakref.WeakSet): 42 | _remove = ... # Getting defined after the super().__init__() call 43 | 44 | def __init__(self, values=()): 45 | super(OrderedWeakset, self).__init__() 46 | 47 | self.data = OrderedSet() 48 | for elem in values: 49 | self.add(elem) 50 | 51 | def move_to_end(self, item): 52 | self.data.move_to_end(weakref.ref(item, self._remove)) 53 | 54 | def move_to_start(self, item): 55 | self.data.move_to_start(weakref.ref(item, self._remove)) 56 | 57 | 58 | 59 | class WidgetBase(ABC): 60 | def __init__(self, win, x, y, width, height, isSubWidget=False): 61 | """ Base for all widgets 62 | 63 | :param win: Surface on which to draw 64 | :type win: pygame.Surface 65 | :param x: X-coordinate of top left 66 | :type x: int 67 | :param y: Y-coordinate of top left 68 | :type y: int 69 | :param width: Width of button 70 | :type width: int 71 | :param height: Height of button 72 | :type height: int 73 | """ 74 | self.win = win 75 | self._x = x 76 | self._y = y 77 | self._width = width 78 | self._height = height 79 | self._isSubWidget = isSubWidget 80 | 81 | self._hidden = False 82 | self._disabled = False 83 | 84 | if not isSubWidget: 85 | WidgetHandler.addWidget(self) 86 | 87 | @abstractmethod 88 | def listen(self, events): 89 | pass 90 | 91 | @abstractmethod 92 | def draw(self): 93 | pass 94 | 95 | def __repr__(self): 96 | return f'{type(self).__name__}(x = {self._x}, y = {self._y}, width = {self._width}, height = {self._height})' 97 | 98 | def contains(self, x, y): 99 | return (self._x < x - self.win.get_abs_offset()[0] < self._x + self._width) and \ 100 | (self._y < y - self.win.get_abs_offset()[1] < self._y + self._height) 101 | 102 | def hide(self): 103 | self._hidden = True 104 | if not self._isSubWidget: 105 | WidgetHandler.moveToBottom(self) 106 | 107 | def show(self): 108 | self._hidden = False 109 | if not self._isSubWidget: 110 | WidgetHandler.moveToTop(self) 111 | 112 | def disable(self): 113 | self._disabled = True 114 | 115 | def enable(self): 116 | self._disabled = False 117 | 118 | def isSubWidget(self): 119 | return self._isSubWidget 120 | 121 | def moveToTop(self): 122 | WidgetHandler.moveToTop(self) 123 | 124 | def moveToBottom(self): 125 | WidgetHandler.moveToBottom(self) 126 | 127 | def moveX(self, x): 128 | self._x += x 129 | 130 | def moveY(self, y): 131 | self._y += y 132 | 133 | def get(self, attr): 134 | """Default setter for any attributes. Call super if overriding 135 | 136 | :param attr: Attribute to get 137 | :return: Value of the attribute 138 | """ 139 | if attr == 'x': 140 | return self._x 141 | 142 | if attr == 'y': 143 | return self._y 144 | 145 | if attr == 'width': 146 | return self._width 147 | 148 | if attr == 'height': 149 | return self._height 150 | 151 | def getX(self): 152 | return self._x 153 | 154 | def getY(self): 155 | return self._y 156 | 157 | def getWidth(self): 158 | return self._width 159 | 160 | def getHeight(self): 161 | return self._height 162 | 163 | def isVisible(self): 164 | return not self._hidden 165 | 166 | def isEnabled(self): 167 | return not self._disabled 168 | 169 | def set(self, attr, value): 170 | """Default setter for any attributes. Call super if overriding 171 | 172 | :param attr: Attribute to set 173 | :param value: Value to set 174 | """ 175 | if attr == 'x': 176 | self._x = value 177 | 178 | if attr == 'y': 179 | self._y = value 180 | 181 | if attr == 'width': 182 | self._width = value 183 | 184 | if attr == 'height': 185 | self._height = value 186 | 187 | def setX(self, x): 188 | self._x = x 189 | 190 | def setY(self, y): 191 | self._y = y 192 | 193 | def setWidth(self, width): 194 | self._width = width 195 | 196 | def setHeight(self, height): 197 | self._height = height 198 | 199 | def setIsSubWidget(self, isSubWidget): 200 | self._isSubWidget = isSubWidget 201 | if isSubWidget: 202 | WidgetHandler.removeWidget(self) 203 | else: 204 | WidgetHandler.addWidget(self) 205 | 206 | 207 | class WidgetHandler: 208 | _widgets: OrderedWeakset[weakref.ref] = OrderedWeakset() 209 | 210 | @staticmethod 211 | def main(events: [Event]) -> None: 212 | blocked = False 213 | 214 | # Conversion is used to prevent errors when widgets are added/removed during iteration a.k.a safe iteration 215 | widgets = list(WidgetHandler._widgets) 216 | 217 | for widget in widgets[::-1]: 218 | if not blocked or not widget.contains(*Mouse.getMousePos()): 219 | widget.listen(events) 220 | 221 | # Ensure widgets covered by others are not affected (widgets created later) 222 | if widget.contains(*Mouse.getMousePos()): # TODO: Unless 'transparent' 223 | blocked = True 224 | 225 | for widget in widgets: 226 | widget.draw() 227 | 228 | @staticmethod 229 | def addWidget(widget: WidgetBase) -> None: 230 | if widget not in WidgetHandler._widgets: 231 | WidgetHandler._widgets.add(widget) 232 | WidgetHandler.moveToTop(widget) 233 | 234 | @staticmethod 235 | def removeWidget(widget: WidgetBase) -> None: 236 | try: 237 | WidgetHandler._widgets.remove(widget) 238 | except ValueError: 239 | print(f'Error: Tried to remove {widget} when {widget} not in WidgetHandler.') 240 | 241 | @staticmethod 242 | def moveToTop(widget: WidgetBase): 243 | try: 244 | WidgetHandler._widgets.move_to_end(widget) 245 | except KeyError: 246 | print(f'Error: Tried to move {widget} to top when {widget} not in WidgetHandler.') 247 | 248 | @staticmethod 249 | def moveToBottom(widget: WidgetBase): 250 | try: 251 | WidgetHandler._widgets.move_to_start(widget) 252 | except KeyError: 253 | print(f'Error: Tried to move {widget} to bottom when {widget} not in WidgetHandler.') 254 | 255 | @staticmethod 256 | def getWidgets() -> [WidgetBase]: 257 | return WidgetHandler._widgets 258 | -------------------------------------------------------------------------------- /site/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |Page not found
89 |Functionality provided and required for all widgets.
120 |Note: Mandatory parameters must be supplied in order.
122 || Parameter | 126 |Description | 127 |Type | 128 |
|---|---|---|
| win | 133 |Surface to be displayed on. | 134 |pygame.Surface | 135 |
| x | 138 |X-coordinate of top left. | 139 |int | 140 |
| y | 143 |Y-coordinate of top left. | 144 |int | 145 |
| width | 148 |Width of button in pixels. | 149 |int | 150 |
| height | 153 |Height of button in pixels. | 154 |int | 155 |
Create an animation by using the default Translate or Resize, inheriting from AnimationBase, or using AnimationBase 120 | directly.
121 |import pygame
123 | from pygame_widgets import Button, Resize
124 |
125 | pygame.init()
126 | win = pygame.display.set_mode((600, 600))
127 |
128 | button = Button(win, 100, 100, 300, 150)
129 |
130 | animation = Resize(button, 3, 200, 200)
131 | animation.start()
132 |
133 | run = True
134 | while run:
135 | events = pygame.event.get()
136 | for event in events:
137 | if event.type == pygame.QUIT:
138 | pygame.quit()
139 | run = False
140 | quit()
141 |
142 | win.fill((255, 255, 255))
143 |
144 | button.listen(events)
145 | button.draw()
146 |
147 | pygame.display.update()
148 |
149 | Over 3 seconds, the width of the button was changed from 300 to 200 and its height from 150 to 200. Since it is 150 | performed on a separate thread, the button is still able to function during the animation.
Allows switching between true and false options
124 |import pygame
126 | from pygame_widgets import Toggle
127 |
128 | pygame.init()
129 | win = pygame.display.set_mode((1000, 600))
130 |
131 | toggle = Toggle(win, 100, 100, 100, 40)
132 |
133 | run = True
134 | while run:
135 | events = pygame.event.get()
136 | for event in events:
137 | if event.type == pygame.QUIT:
138 | pygame.quit()
139 | run = False
140 | quit()
141 |
142 | win.fill((255, 255, 255))
143 |
144 | toggle.listen(events)
145 | toggle.draw()
146 |
147 | pygame.display.update()
148 |
149 | | Parameter | 154 |Description | 155 |Type | 156 |Default | 157 |
|---|---|---|---|
| startOn | 162 |Default value. | 163 |bool | 164 |False | 165 |
| onColour | 168 |Colour of toggle when on. | 169 |(int, int, int) | 170 |(141, 185, 244) | 171 |
| offColour | 174 |Colour of toggle when off. | 175 |(int, int, int) | 176 |(150, 150, 150) | 177 |
| handleOnColour | 180 |Thickness of toggle handle when on. | 181 |(int, int, int) | 182 |(26, 115, 232) | 183 |
| handleOffColour | 186 |Thickness of toggle handle when off. | 187 |(int, int, int) | 188 |(200, 200, 200) | 189 |
| handleRadius | 192 |Radius of handle. | 193 |int | 194 |height / 1.3 | 195 |
Displays a continuously changing percentage
128 |import pygame
130 | import time
131 | from pygame_widgets import ProgressBar
132 |
133 | startTime = time.time()
134 |
135 | pygame.init()
136 | win = pygame.display.set_mode((1000, 600))
137 |
138 | progressBar = ProgressBar(win, 100, 100, 500, 40, lambda: 1 - (time.time() - startTime) / 10, curved=True)
139 |
140 | run = True
141 | while run:
142 | events = pygame.event.get()
143 | for event in events:
144 | if event.type == pygame.QUIT:
145 | pygame.quit()
146 | run = False
147 | quit()
148 |
149 | win.fill((255, 255, 255))
150 |
151 | progressBar.draw()
152 |
153 | pygame.display.update()
154 |
155 | This progress bar uses time to fill up, however, the progress function can be replaced by 156 | any other function call that provides a percentage.
157 || Parameter | 162 |Description | 163 |Type | 164 |
|---|---|---|
| progress | 169 |Function that defines the percentage of the bar filled. | 170 |function -> float | 171 |
| Parameter | 179 |Description | 180 |Type | 181 |Default | 182 |
|---|---|---|---|
| curved | 187 |Adds curved ends to the progress bar. | 188 |bool | 189 |False | 190 |
| completedColour | 193 |Colour of completed section of progress bar. | 194 |(int, int, int) | 195 |(0, 200, 0) | 196 |
| incompletedColour | 199 |Colour of incompleted section of progress bar. | 200 |(int, int, int) | 201 |(100, 100, 100) | 202 |
A helper module for common widgets that may be required in developing applications with Pygame.
133 |Ensure that Python 3 and pip are installed and added to your environment PATH.
140 |python -m pip install pygame-widgets
Open a Python console and run the following command.
142 |import pygame_widgets
If you receive no errors, the installation was successful.
144 |Any contribution to this project would be greatly appreciated. 159 | This can include:
160 |If applicable, you should make any changes in a forked repository and then create a pull 167 | request once the changes are complete and preferably tested if possible.
168 |Note: If writing any code, please attempt to follow the Code Style Guide
A slider for discrete numeric value selection
124 |
import pygame
127 | from pygame_widgets import Slider, TextBox
128 |
129 | pygame.init()
130 | win = pygame.display.set_mode((1000, 600))
131 |
132 | slider = Slider(win, 100, 100, 800, 40, min=0, max=99, step=1)
133 | output = TextBox(win, 475, 200, 60, 50, fontSize=30)
134 |
135 | run = True
136 | while run:
137 | events = pygame.event.get()
138 | for event in events:
139 | if event.type == pygame.QUIT:
140 | pygame.quit()
141 | run = False
142 | quit()
143 |
144 | win.fill((255, 255, 255))
145 |
146 | slider.listen(events)
147 | slider.draw()
148 |
149 | output.setText(str(slider.getValue()))
150 |
151 | output.draw()
152 |
153 | pygame.display.update()
154 |
155 | As you can see, TextBox can be used to display text as well, by not calling its listen method.
156 || Parameter | 161 |Description | 162 |Type | 163 |Default | 164 |
|---|---|---|---|
| min | 169 |Minimum value of the slider (left). | 170 |int or float | 171 |0 | 172 |
| max | 175 |Maximum value of the slider (right). | 176 |int or float | 177 |99 | 178 |
| step | 181 |Value to increment by. | 182 |int or float | 183 |1 | 184 |
| colour | 187 |Colour of slider. | 188 |(int, int, int) | 189 |(200, 200, 200) | 190 |
| handleColour | 193 |Colour of handle. | 194 |(int, int, int) | 195 |(0, 0, 0) | 196 |
| initial | 199 |Initial value of the slider. | 200 |int or float | 201 |Average of min and max | 202 |
| handleRadius | 205 |Radius of handle. | 206 |int | 207 |height / 1.3 | 208 |
| curved | 211 |Add curved ends to the slider. | 212 |bool | 213 |True | 214 |
A box for text input or display
124 |
import pygame
127 | from pygame_widgets import TextBox
128 |
129 |
130 | def output():
131 | # Get text in the textbox
132 | print(textbox.getText())
133 |
134 |
135 | pygame.init()
136 | win = pygame.display.set_mode((1000, 600))
137 |
138 | textbox = TextBox(win, 100, 100, 800, 80, fontSize=50,
139 | borderColour=(255, 0, 0), textColour=(0, 200, 0),
140 | onSubmit=output, radius=10, borderThickness=5)
141 |
142 | run = True
143 | while run:
144 | events = pygame.event.get()
145 | for event in events:
146 | if event.type == pygame.QUIT:
147 | pygame.quit()
148 | run = False
149 | quit()
150 |
151 | win.fill((255, 255, 255))
152 |
153 | textbox.listen(events)
154 | textbox.draw()
155 |
156 | pygame.display.update()
157 |
158 | | Parameter | 163 |Description | 164 |Type | 165 |Default | 166 |
|---|---|---|---|
| colour | 171 |Background colour. | 172 |(int, int, int) | 173 |(220, 220, 220) | 174 |
| textColour | 177 |Colour of text. | 178 |(int, int, int) | 179 |(0, 0, 0) | 180 |
| borderColour | 183 |Colour of border. | 184 |(int, int, int) | 185 |(0, 0, 0) | 186 |
| borderThickness | 189 |Thickness of border. | 190 |int | 191 |3 | 192 |
| radius | 195 |Border radius. Set to 0 for no radius. | 196 |int | 197 |0 | 198 |
| onSubmit | 201 |Function to be called when return / enter is pressed. | 202 |function | 203 |None | 204 |
| onSubmitParams | 207 |Parameters to be fed into onSubmit function. | 208 |(*any) | 209 |() | 210 |
| onTextChanged | 213 |Function to be called when the text in the box is changed. | 214 |function | 215 |None | 216 |
| onTextChangedParams | 219 |Parameters to be fed into onTextChanged function. | 220 |(*any) | 221 |() | 222 |
| placeholderText | 225 |Text to be displayed when empty. | 226 |str | 227 |'' | 228 |
| fontSize | 231 |Size of text. | 232 |int | 233 |20 | 234 |
| font | 237 |Font of text. | 238 |pygame.font.Font | 239 |Calibri | 240 |