├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.txt ├── __init__.py ├── _utils ├── __init__.py ├── colorscomputing.py ├── functions.py ├── images.py ├── interpolation.py ├── readfile.py ├── rectscomputing.py └── strhandler.py ├── data ├── Carlito │ ├── Carlito-Bold.ttf │ ├── Carlito-BoldItalic.ttf │ ├── Carlito-Italic.ttf │ ├── Carlito-Regular.ttf │ └── LICENSE ├── arrow.bmp ├── check_box.bmp ├── check_radio.bmp ├── checkbutton.bmp ├── folder.png ├── heart_empty.png ├── heart_full.png ├── painting.jpg ├── radiobutton.bmp ├── smoke.png └── thorpy_icon.png ├── documentation ├── about.html ├── convertmake.py ├── css │ └── style.css ├── documentation.html ├── documentation │ └── userguide │ │ ├── cheatsheet.html │ │ ├── elements.html │ │ ├── painterstitle.html │ │ ├── philosophy.html │ │ ├── proc.py │ │ └── reactions.html ├── downloads.html ├── elements.html ├── examples.html ├── examples │ ├── alerts.html │ ├── animated_gif.html │ ├── boat_example.png │ ├── character.png │ ├── choices.html │ ├── fx.html │ ├── fx.png │ ├── generatehtml.py │ ├── hover.png │ ├── image_buttons.html │ ├── launching.html │ ├── myGif.gif │ ├── normal.png │ ├── overview.html │ ├── overview.png │ ├── pools.html │ ├── pressed.png │ ├── screen100.png │ ├── screen600.png │ ├── screen800.png │ ├── shadows.html │ ├── shadows.png │ ├── style.html │ ├── style.png │ ├── submenus.html │ ├── submenus.png │ ├── template.html │ └── userchoices2.html ├── faq.html ├── favicon.ico ├── features.html ├── fonts.css ├── highlighter.py ├── images │ ├── examplegraph - Copie.png │ ├── examplegraph.png │ ├── flecheblanchedroite.png │ ├── thorpy.png │ ├── thorpy_head.png │ └── tuto_button_bckgr.png ├── img │ ├── apps │ │ ├── container-box.gif │ │ ├── pcp-box.gif │ │ ├── pd-box.gif │ │ ├── pdfwl-box.gif │ │ ├── poa-box.gif │ │ └── server-box.gif │ ├── bullet.gif │ ├── globe.png │ ├── icons │ │ ├── aspdotnet.png │ │ ├── fastcgi.png │ │ ├── perl.png │ │ ├── php.png │ │ ├── python.png │ │ └── ssi.png │ ├── p-box.png │ ├── panel-logo.png │ ├── parallels-logo.png │ ├── th.png │ └── top-bottom.png ├── index.html ├── informations.html ├── license.html ├── minigame.html ├── more.html ├── prism.css ├── prism.js ├── screenshots.html ├── style.css ├── tutorials.html └── tutorials │ ├── _old_install.html │ ├── distribution.html │ ├── distribution │ └── img1.png │ ├── helloworld.html │ ├── helloworld │ ├── main.png │ ├── step2.png │ └── step3.png │ ├── include.html │ ├── install.html │ ├── minigame.html │ ├── minigame │ ├── game.png │ ├── menu.png │ └── present.png │ ├── painters.html │ ├── painters │ ├── a.png │ ├── b.png │ └── c.png │ ├── reactions.html │ ├── reactions │ └── step1const.html │ ├── reallife.html │ ├── reallife │ └── step1.png │ ├── storage.html │ └── storage │ ├── basics1.png │ ├── basics2.png │ ├── boxes.png │ └── manual.png ├── elements ├── __init__.py ├── _explorerutils │ ├── __init__.py │ ├── _dirviewer.py │ └── _pathelement.py ├── _hoverutils │ ├── __init__.py │ └── _hovergetter.py ├── _inserterutils │ ├── __init__.py │ ├── _cursor.py │ └── _insertwriter.py ├── _makeuputils │ ├── __init__.py │ ├── _halo.py │ └── _shadow.py ├── _sliderutils │ ├── __init__.py │ ├── _dragger.py │ └── _shifters.py ├── _wrappers.py ├── animatedgif.py ├── background.py ├── box.py ├── browser.py ├── browserlight.py ├── checker.py ├── clickable.py ├── colorsetter.py ├── ddlf.py ├── dialog.py ├── draggable.py ├── element.py ├── ghost.py ├── hoverable.py ├── hoverzone.py ├── image.py ├── inserter.py ├── keypressable.py ├── keytogglable.py ├── launchers │ ├── __init__.py │ ├── browserlauncher.py │ ├── colorsetterlauncher.py │ ├── dropdownlistlauncher.py │ └── paramsetterlauncher.py ├── lifebar.py ├── lift.py ├── line.py ├── paramsetter.py ├── pressable.py ├── slider.py ├── slidersetter.py ├── text.py └── togglable.py ├── examples ├── _mygame.py ├── advancedmenus.py ├── alerts.py ├── animatedGif.py ├── basicstyling.py ├── coupling.py ├── design.py ├── guessthenumber.py ├── imagebuttons.py ├── lifeBar.py ├── overview.py ├── radiobuttons.py ├── reactions.py ├── shadows.py ├── specialeffects.py ├── storage.py ├── submenus.py ├── userchoices.py ├── userchoices2.py └── usewithpygame.py ├── gamestools ├── __init__.py ├── basegrid.py ├── commands.py ├── drawgrid.py ├── fx.py ├── grid.py ├── hud.py ├── monitoring.py ├── physics.py ├── sound.py └── writing.py ├── menus ├── __init__.py ├── basicmenu.py └── tickedmenu.py ├── miscgui ├── __init__.py ├── _ghoststate.py ├── _screened.py ├── application.py ├── constants.py ├── functions.py ├── initializer.py ├── keyer.py ├── launchers │ ├── __init__.py │ └── launcher.py ├── metadata.py ├── painterstyle.py ├── parameters.py ├── pools.py ├── reaction.py ├── state.py ├── storage.py ├── style.py ├── textpacker.py ├── theme.py ├── title.py ├── utils.py └── varset.py ├── painting ├── __init__.py ├── fusionner.py ├── graphics.py ├── makeup.py ├── mousecursor.py ├── painters │ ├── __init__.py │ ├── basicframe.py │ ├── classicframe.py │ ├── classicround.py │ ├── imageframe.py │ ├── inserterframe.py │ ├── optionnal │ │ ├── __init__.py │ │ ├── _deprecated_grid.py │ │ ├── grid.py │ │ ├── human.py │ │ ├── illuminer.py │ │ ├── rectframe.py │ │ ├── rectreflect.py │ │ └── roundreflect.py │ ├── painter.py │ ├── roundrect.py │ └── templateframe.py ├── pilgraphics.py └── writer.py ├── setup.cfg ├── setup.py └── testmodule.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | build/* 4 | dist/* 5 | *egg-info* 6 | *__pycache_ 7 | *Thumbs.db* 8 | *pour_moi* 9 | thorpy.zip 10 | *~py 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yann Thorimbert 4 | 5 | E-mail: yann.thorimbert@gmail.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include thorpy/data/* -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | # Thorpy - Yann Thorimbert - yann.thorimbert@gmail.com 2 | 3 | Thorpy is a free GUI library intended for use with pygame. More informations on http://www.thorpy.org. 4 | 5 | ThorPy 1 is discontinued ! Thorpy 2 is out. 6 | 7 | -------------------------------------------------------------------------------- /_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/_utils/__init__.py -------------------------------------------------------------------------------- /_utils/interpolation.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def get_y_quad(x, beg, end, M): #quadratic interpolation 4 | W = end - beg 5 | actual_x = x - beg 6 | return M*actual_x*(2-actual_x/W)/W 7 | 8 | def get_y_linear(x, beg, end, M): #linear interpolation 9 | return (x-beg) * M / (end-beg) 10 | 11 | def get_y_exp(x, beg, end, M, B=1.): 12 | W = end - beg 13 | actual_x = x - beg 14 | A = M / (math.exp(B*W) - 1.) 15 | return A * (math.exp(B*actual_x) - 1.) 16 | 17 | 18 | _functions = {"linear":get_y_linear, 19 | "quadratic":get_y_quad, 20 | "exponential":get_y_exp} 21 | 22 | def get_y(x, beg, end, M=255, before=0, after=255, mode="linear"): 23 | if x < beg: 24 | return before 25 | elif x > end: 26 | return after 27 | return _functions[mode](x,beg,end,M) -------------------------------------------------------------------------------- /_utils/rectscomputing.py: -------------------------------------------------------------------------------- 1 | """Old and not used""" 2 | 3 | from pygame import Rect 4 | 5 | 6 | def get_rel_pos_topleft(parent, child): 7 | dx = child.x - parent.x 8 | dy = child.y - parent.y 9 | return (dx, dy) 10 | 11 | 12 | def getRectsSizes(rects): 13 | r = list([]) 14 | for rect in rects: 15 | r.append(Rect((0,0), rect.size)) 16 | return r 17 | 18 | def getRectsSizes2(rects): 19 | r = list([]) 20 | for rect in rects: 21 | r.append(Rect((0,0), rect.size)) 22 | return r 23 | 24 | def centerRect(fixed, toMove): 25 | toMove.center = fixed.center 26 | 27 | def add(r1, r2): 28 | return Rect(r1.x + r2.x, r1.y + r2.y) 29 | 30 | def getCorners(r): 31 | return [r.topleft, r.topright, r.bottomleft, r.bottomright] 32 | 33 | def cut_static(home, visitor): 34 | if not home.colliderect(visitor): 35 | return [] 36 | #left 37 | w = visitor.left - home.left 38 | left = Rect(home.topleft, (w,home.h)) 39 | #right 40 | w = home.right - visitor.right 41 | x = visitor.right 42 | right = Rect((x,home.top), (w,home.h)) 43 | #top 44 | h = visitor.top - home.top 45 | x = visitor.left 46 | top = Rect((x,home.top), (visitor.w, h)) 47 | #bottom 48 | h = home.bottom - visitor.bottom 49 | bottom = Rect(visitor.bottomleft, (visitor.w, h)) 50 | 51 | parts = [left, right, top, bottom] 52 | toReturn = [] 53 | for part in parts: 54 | if part.w > 0 and part.h > 0: 55 | toReturn.append(part) 56 | return toReturn 57 | 58 | def rectSet(rectList): 59 | """Returns a list of rect without doublons""" 60 | toReturn = [] 61 | for rect in rectList: 62 | if rect not in toReturn: 63 | toReturn.append(rect) 64 | return toReturn 65 | 66 | def sameCorners(r1, r2): 67 | """Returns number of corners shared""" 68 | counter = 0 69 | for corner1 in getCorners(r1): 70 | for corner2 in getCorners(r2): 71 | if corner1 == corner2: 72 | counter += 1 73 | return counter 74 | 75 | def sameDimensions(r1, r2): 76 | return (r1.w == r2.w) + (r1.h == r2.h) 77 | 78 | def get_top_coords(rect): 79 | bottomleft = (rect.left, rect.bottom-2) 80 | topleft = (rect.left, rect.top) 81 | topright = (rect.right-1, rect.top) 82 | return (bottomleft,topleft, topright) 83 | 84 | def get_bottom_coords(rect): 85 | bottomleft = (rect.left, rect.bottom-1) 86 | bottomright = (rect.right-1, rect.bottom-1) 87 | topright = (rect.right-1, rect.top) 88 | return (bottomleft, bottomright, topright) 89 | 90 | ##ALIGNEMENTS## 91 | 92 | def alignV(elements, value): 93 | for elt in elements: 94 | elt.center = (value, elt.centery) 95 | 96 | def alignLeft(elements, value): 97 | for elt in elements: 98 | elt.topleft = (value, elt.top) 99 | 100 | def alignRight(elements, value): 101 | for elt in elements._elements: 102 | elt.topright = (value, elt.top) 103 | 104 | def alignH(elements, value): 105 | for elt in elements: 106 | elt.center = (elt.centerx, value) 107 | 108 | def alignTop(elements, value): 109 | for elt in elements: 110 | elt.topleft = (elt.left, value) 111 | 112 | def alignBottom(elements, value): 113 | for elt in elements: 114 | elt.bottomleft = (elt.left, value) 115 | 116 | def spaceX(elements, x0=-5, gap=5): 117 | """Changes the horizontal coordinates of all to make them 118 | spaced of each other, beginning at coordinate""" 119 | previousX = x0 120 | for elt in elements: 121 | elt.left = previousX + gap 122 | previousX = elt.right 123 | 124 | def spaceY(elements, y0=-5, gap=5): 125 | """Changes the vertical coordinates of all to make them 126 | spaced of each other, beginning at coordinate""" 127 | previousY = y0 128 | for elt in elements: 129 | elt.top = previousY + gap 130 | previousY = elt.bottom 131 | -------------------------------------------------------------------------------- /data/Carlito/Carlito-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/Carlito/Carlito-Bold.ttf -------------------------------------------------------------------------------- /data/Carlito/Carlito-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/Carlito/Carlito-BoldItalic.ttf -------------------------------------------------------------------------------- /data/Carlito/Carlito-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/Carlito/Carlito-Italic.ttf -------------------------------------------------------------------------------- /data/Carlito/Carlito-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/Carlito/Carlito-Regular.ttf -------------------------------------------------------------------------------- /data/arrow.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/arrow.bmp -------------------------------------------------------------------------------- /data/check_box.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/check_box.bmp -------------------------------------------------------------------------------- /data/check_radio.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/check_radio.bmp -------------------------------------------------------------------------------- /data/checkbutton.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/checkbutton.bmp -------------------------------------------------------------------------------- /data/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/folder.png -------------------------------------------------------------------------------- /data/heart_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/heart_empty.png -------------------------------------------------------------------------------- /data/heart_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/heart_full.png -------------------------------------------------------------------------------- /data/painting.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/painting.jpg -------------------------------------------------------------------------------- /data/radiobutton.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/radiobutton.bmp -------------------------------------------------------------------------------- /data/smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/smoke.png -------------------------------------------------------------------------------- /data/thorpy_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/data/thorpy_icon.png -------------------------------------------------------------------------------- /documentation/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 |
39 |

About

40 |

ThorPy is a GUI library for the excellent pygame, which is a python library based on the SDL. Pygame is a library for games and applications coding.

41 |

ThorPy is written in pure Python (2.7 and 3.X) and is therefore available on all plateforms for which pygame is available. However, for some optional effects like blur and shadows, you will need NumPy and PIL (see installation tutorial for more).

42 |

The library is free, open-source and distributed under the MIT License.

43 |
44 |
45 | 46 | 49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /documentation/convertmake.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | pattern_src = '.make(' 4 | pattern_dst = '(' 5 | 6 | def replace_in_file(fn, src, dst): 7 | with open(fn,"r") as f: 8 | new_lines = [line.replace(src, dst) for line in f.readlines()] 9 | with open(fn,"w") as f: 10 | for line in new_lines: 11 | f.write(line) 12 | 13 | 14 | for dir in ["examples", "tutorials"]: 15 | for fn in os.listdir(dir): 16 | if fn.endswith(".html"): 17 | replace_in_file(os.path.join(dir,fn), pattern_src, pattern_dst) 18 | -------------------------------------------------------------------------------- /documentation/documentation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 | 62 |
63 | 64 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /documentation/documentation/userguide/painterstitle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 |
39 |

User Guide - Painters and Titles

40 |

What are Painters and Titles

41 |

In order to keep the logical and graphical component well separated, ThorPy uses a specific family of objects to generate the graphical part of an element. This allow, for example, to use the so-called 'themes' and to change the style of the whole program by adding only one line to the code.

42 |

Before getting into the heart of the matter, you have to know that at a certain time, element is into a certain state. For example, thorpy.Element instances have only one state by default, which is thorpy.constants.STATE_NORMAL, while thorpy.Clickable can in addition be in the state thorpy.constants.STATE_PRESSED. In fact, you are even free to define other states. The point is that to each state belongs a different style ; when the element changes of state, this changes how it looks like. To a given state correpond a given Painter instance and a given Title instance.

43 |

When you call the finish method of an element, you tell to this element that he can begin its graphical existence since you have specified all its graphical attributes. In fact, two things basically happens: first, the element call the Painters of all its states and tell them to produce the corresponding pygame surface. Then he do exactly the same for the Titles of each state. Finally, Painter's surface and Title's surface are merged in order to produce the actual surface that will be drawn on the screen.

44 |

You can find a tutorial on Painters and Titles in order to learn how to use them in practice.

45 |
46 |
47 | 48 | 51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /documentation/documentation/userguide/proc.py: -------------------------------------------------------------------------------- 1 | import os 2 | os.remove("./new.html") 3 | 4 | f0 = open("./cheatsheet.html", "r") 5 | f1 = open("./new.html", "w") 6 | 7 | 8 | 9 | for line in f0.readlines(): 10 | if '' in line: 11 | newline = line.replace('', '') 12 | newline = newline.replace('', '') 13 | else: 14 | newline = line 15 | f1.write(newline) 16 | 17 | f0.close() 18 | f1.close() -------------------------------------------------------------------------------- /documentation/downloads.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 | 45 |
46 | 47 | 50 |
51 | 52 | 53 | -------------------------------------------------------------------------------- /documentation/examples/animated_gif.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ThorPy 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 | 22 |

ThorPy

23 |

A GUI library for pygame

24 |
25 | 26 | 36 |
37 | 38 |
39 |
40 |

Examples - Animated gif

41 | 42 | 43 |

Full code

44 | 45 | 46 |

The code below shows how to set up an animated gif.

47 |
48 | 49 |

50 |         import thorpy
51 | 
52 |         application = thorpy.Application((800, 600), "Example of animated gif")
53 | 
54 |         gif_element = thorpy.AnimatedGif("../documentation/examples/myGif.gif")
55 |         gif_element.center()
56 | 
57 |         menu = thorpy.Menu(gif_element)
58 |         menu.play()
59 | 
60 |         application.quit()
61 | 
62 |         """
63 |         Other parameters to pass to the constructor of AnimatedGif:
64 |                 : the path to the image.
65 |                 : if path is None, use this color instead of image.
66 |                 : increase this parameter to lower the gif speed.
67 |                 : number of times the gif is played
68 |         """
69 |     
70 |
71 | 72 | 73 | 74 | 75 |
76 | 77 | 78 | 79 | 80 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /documentation/examples/boat_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/boat_example.png -------------------------------------------------------------------------------- /documentation/examples/character.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/character.png -------------------------------------------------------------------------------- /documentation/examples/fx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/fx.png -------------------------------------------------------------------------------- /documentation/examples/hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/hover.png -------------------------------------------------------------------------------- /documentation/examples/myGif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/myGif.gif -------------------------------------------------------------------------------- /documentation/examples/normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/normal.png -------------------------------------------------------------------------------- /documentation/examples/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/overview.png -------------------------------------------------------------------------------- /documentation/examples/pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/pressed.png -------------------------------------------------------------------------------- /documentation/examples/screen100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/screen100.png -------------------------------------------------------------------------------- /documentation/examples/screen600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/screen600.png -------------------------------------------------------------------------------- /documentation/examples/screen800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/screen800.png -------------------------------------------------------------------------------- /documentation/examples/shadows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/shadows.png -------------------------------------------------------------------------------- /documentation/examples/style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/style.png -------------------------------------------------------------------------------- /documentation/examples/submenus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/examples/submenus.png -------------------------------------------------------------------------------- /documentation/examples/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 |
46 |
47 | 48 |

ThorPy

49 |

A GUI library for pygame

50 |
51 | 52 | 62 |
63 | 64 |
65 |
66 |

Examples - 67 | ###TITLE### 68 |

69 | 70 | 71 |

Full code

72 | ###TEXT### 73 | 74 |
75 |
76 | ###LINES### 77 |
78 | 79 |
80 | ###CODE### 81 |
82 | 83 | 84 | ###IMG### 85 |
86 | 87 | 88 | 89 | 90 | 93 | 94 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /documentation/faq.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 |
39 |

FAQ

40 |

Under construction...

41 |
42 |
43 | 44 | 47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /documentation/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/favicon.ico -------------------------------------------------------------------------------- /documentation/features.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 |
39 |

Features

40 |

ThorPy's key features

41 |

42 |

43 |
44 |
45 | 46 | 49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /documentation/highlighter.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | with open(sys.argv[1],"r") as f: 4 | codelines = f.readlines() 5 | 6 | with open(sys.argv[2],"r") as f: 7 | oldlines = f.readlines() 8 | 9 | with open(sys.argv[2],"w") as f: 10 | for line in oldlines: 11 | if "<***INSERT_CODE***>" in line: 12 | for i,codeline in enumerate(codelines): 13 | f.write(str(i)+" "+codeline) 14 | else: 15 | f.write(line) 16 | -------------------------------------------------------------------------------- /documentation/images/examplegraph - Copie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/images/examplegraph - Copie.png -------------------------------------------------------------------------------- /documentation/images/examplegraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/images/examplegraph.png -------------------------------------------------------------------------------- /documentation/images/flecheblanchedroite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/images/flecheblanchedroite.png -------------------------------------------------------------------------------- /documentation/images/thorpy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/images/thorpy.png -------------------------------------------------------------------------------- /documentation/images/thorpy_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/images/thorpy_head.png -------------------------------------------------------------------------------- /documentation/images/tuto_button_bckgr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/images/tuto_button_bckgr.png -------------------------------------------------------------------------------- /documentation/img/apps/container-box.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/apps/container-box.gif -------------------------------------------------------------------------------- /documentation/img/apps/pcp-box.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/apps/pcp-box.gif -------------------------------------------------------------------------------- /documentation/img/apps/pd-box.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/apps/pd-box.gif -------------------------------------------------------------------------------- /documentation/img/apps/pdfwl-box.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/apps/pdfwl-box.gif -------------------------------------------------------------------------------- /documentation/img/apps/poa-box.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/apps/poa-box.gif -------------------------------------------------------------------------------- /documentation/img/apps/server-box.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/apps/server-box.gif -------------------------------------------------------------------------------- /documentation/img/bullet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/bullet.gif -------------------------------------------------------------------------------- /documentation/img/globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/globe.png -------------------------------------------------------------------------------- /documentation/img/icons/aspdotnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/icons/aspdotnet.png -------------------------------------------------------------------------------- /documentation/img/icons/fastcgi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/icons/fastcgi.png -------------------------------------------------------------------------------- /documentation/img/icons/perl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/icons/perl.png -------------------------------------------------------------------------------- /documentation/img/icons/php.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/icons/php.png -------------------------------------------------------------------------------- /documentation/img/icons/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/icons/python.png -------------------------------------------------------------------------------- /documentation/img/icons/ssi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/icons/ssi.png -------------------------------------------------------------------------------- /documentation/img/p-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/p-box.png -------------------------------------------------------------------------------- /documentation/img/panel-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/panel-logo.png -------------------------------------------------------------------------------- /documentation/img/parallels-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/parallels-logo.png -------------------------------------------------------------------------------- /documentation/img/th.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/th.png -------------------------------------------------------------------------------- /documentation/img/top-bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/img/top-bottom.png -------------------------------------------------------------------------------- /documentation/informations.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Patricia & Grégoire 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 35 |
36 | 40 |
41 |
42 |

Wedding day

43 | 44 |
45 |

46 |
47 | 48 | 66 | 67 | 73 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /documentation/license.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 |
39 |

License

40 |

The MIT License (MIT)

41 | 42 |

Copyright (c) 2016 Yann Thorimbert

43 | 44 |

E-mail: yann.thorimbert@gmail.com

45 | 46 |

Permission is hereby granted, free of charge, to any person obtaining a copy 47 | of this software and associated documentation files (the "Software"), to deal 48 | in the Software without restriction, including without limitation the rights 49 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 50 | copies of the Software, and to permit persons to whom the Software is 51 | furnished to do so, subject to the following conditions:

52 | 53 |

The above copyright notice and this permission notice shall be included in all 54 | copies or substantial portions of the Software.

55 | 56 |

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 57 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 58 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 59 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 60 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 61 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 62 | SOFTWARE.

63 |
64 |
65 | 66 | 69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /documentation/more.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 |
39 |

More...

40 |

Forum

41 |

ThorPy's forum is the place where you can find help and share your projects and ideas.

42 | 44 |

Real games

45 |

You can see some real applications examples on the "Full application example" section of this page.

46 |

Other projects

47 |

Other projects of the author: 48 |

53 |

54 |
55 |
56 | 57 | 60 |
61 | 62 | 63 | -------------------------------------------------------------------------------- /documentation/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.15.0 2 | https://prismjs.com/download.html#themes=prism&languages=python */ 3 | /** 4 | * prism.js default theme for JavaScript, CSS and HTML 5 | * Based on dabblet (http://dabblet.com) 6 | * @author Lea Verou 7 | */ 8 | 9 | code[class*="language-"], 10 | pre[class*="language-"] { 11 | color: black; 12 | background: none; 13 | text-shadow: 0 1px white; 14 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 15 | font-size: 1em; 16 | text-align: left; 17 | white-space: pre; 18 | word-spacing: normal; 19 | word-break: normal; 20 | word-wrap: normal; 21 | line-height: 1.5; 22 | 23 | -moz-tab-size: 4; 24 | -o-tab-size: 4; 25 | tab-size: 4; 26 | 27 | -webkit-hyphens: none; 28 | -moz-hyphens: none; 29 | -ms-hyphens: none; 30 | hyphens: none; 31 | } 32 | 33 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 34 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 35 | text-shadow: none; 36 | background: #b3d4fc; 37 | } 38 | 39 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 40 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 41 | text-shadow: none; 42 | background: #b3d4fc; 43 | } 44 | 45 | @media print { 46 | code[class*="language-"], 47 | pre[class*="language-"] { 48 | text-shadow: none; 49 | } 50 | } 51 | 52 | /* Code blocks */ 53 | pre[class*="language-"] { 54 | padding: 1em; 55 | margin: .5em 0; 56 | overflow: auto; 57 | } 58 | 59 | :not(pre) > code[class*="language-"], 60 | pre[class*="language-"] { 61 | background: #f5f2f0; 62 | } 63 | 64 | /* Inline code */ 65 | :not(pre) > code[class*="language-"] { 66 | padding: .1em; 67 | border-radius: .3em; 68 | white-space: normal; 69 | } 70 | 71 | .token.comment, 72 | .token.prolog, 73 | .token.doctype, 74 | .token.cdata { 75 | color: slategray; 76 | } 77 | 78 | .token.punctuation { 79 | color: #999; 80 | } 81 | 82 | .namespace { 83 | opacity: .7; 84 | } 85 | 86 | .token.property, 87 | .token.tag, 88 | .token.boolean, 89 | .token.number, 90 | .token.constant, 91 | .token.symbol, 92 | .token.deleted { 93 | color: #905; 94 | } 95 | 96 | .token.selector, 97 | .token.attr-name, 98 | .token.string, 99 | .token.char, 100 | .token.builtin, 101 | .token.inserted { 102 | color: #690; 103 | } 104 | 105 | .token.operator, 106 | .token.entity, 107 | .token.url, 108 | .language-css .token.string, 109 | .style .token.string { 110 | color: #9a6e3a; 111 | background: hsla(0, 0%, 100%, .5); 112 | } 113 | 114 | .token.atrule, 115 | .token.attr-value, 116 | .token.keyword { 117 | color: #07a; 118 | } 119 | 120 | .token.function, 121 | .token.class-name { 122 | color: #DD4A68; 123 | } 124 | 125 | .token.regex, 126 | .token.important, 127 | .token.variable { 128 | color: #e90; 129 | } 130 | 131 | .token.important, 132 | .token.bold { 133 | font-weight: bold; 134 | } 135 | .token.italic { 136 | font-style: italic; 137 | } 138 | 139 | .token.entity { 140 | cursor: help; 141 | } 142 | 143 | -------------------------------------------------------------------------------- /documentation/screenshots.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 |
39 |

Screenshots

40 |

Under construction...

41 |
42 |
43 | 44 | 47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /documentation/tutorials.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | ThorPy 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |

ThorPy

22 |

A GUI library for pygame

23 |
24 | 25 | 35 |
36 | 37 |
38 |
39 |

Tutorials

40 |

Below are summarized the avalaible tutorials. You can also find many examples that may fit your needs. Finally, the documentation contains more technical informations about the objects provided by the library.

41 | 63 |
64 |
65 | 66 | 69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /documentation/tutorials/distribution/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/distribution/img1.png -------------------------------------------------------------------------------- /documentation/tutorials/helloworld/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/helloworld/main.png -------------------------------------------------------------------------------- /documentation/tutorials/helloworld/step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/helloworld/step2.png -------------------------------------------------------------------------------- /documentation/tutorials/helloworld/step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/helloworld/step3.png -------------------------------------------------------------------------------- /documentation/tutorials/minigame/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/minigame/game.png -------------------------------------------------------------------------------- /documentation/tutorials/minigame/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/minigame/menu.png -------------------------------------------------------------------------------- /documentation/tutorials/minigame/present.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/minigame/present.png -------------------------------------------------------------------------------- /documentation/tutorials/painters/a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/painters/a.png -------------------------------------------------------------------------------- /documentation/tutorials/painters/b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/painters/b.png -------------------------------------------------------------------------------- /documentation/tutorials/painters/c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/painters/c.png -------------------------------------------------------------------------------- /documentation/tutorials/reallife/step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/reallife/step1.png -------------------------------------------------------------------------------- /documentation/tutorials/storage/basics1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/storage/basics1.png -------------------------------------------------------------------------------- /documentation/tutorials/storage/basics2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/storage/basics2.png -------------------------------------------------------------------------------- /documentation/tutorials/storage/boxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/storage/boxes.png -------------------------------------------------------------------------------- /documentation/tutorials/storage/manual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/documentation/tutorials/storage/manual.png -------------------------------------------------------------------------------- /elements/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/elements/__init__.py -------------------------------------------------------------------------------- /elements/_explorerutils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/elements/_explorerutils/__init__.py -------------------------------------------------------------------------------- /elements/_explorerutils/_pathelement.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from thorpy.elements.text import OneLineText 4 | from thorpy.elements.clickable import Clickable 5 | from thorpy.miscgui.storage import h_store 6 | from thorpy.miscgui import functions, style 7 | 8 | class PathElement(OneLineText): 9 | 10 | def __init__(self, father, abspath,finish=True): 11 | OneLineText.__init__(self) 12 | self.father = father 13 | self._path = father.path 14 | self.abspath = abspath 15 | self._n = None 16 | self._path_list = self._get_strs() 17 | self._path = "".join(self._path_list) 18 | if finish: 19 | self.finish() 20 | 21 | def finish(self): 22 | OneLineText.finish(self) 23 | 24 | def _get_strs(self): 25 | if self.abspath: 26 | path = os.path.abspath(self._path) 27 | else: 28 | path = str(self._path) 29 | path = os.path.normpath(path) 30 | path = path.split(os.sep) 31 | path = [s+"/" for s in path] 32 | return path 33 | 34 | def _reaction_path(self, n): 35 | if n != self._n: 36 | self._path_list = self._path_list[0:n+1] 37 | self._path = "".join(self._path_list) 38 | ycoord = self._elements[0].get_storer_rect().centery 39 | self._set_path_elements(ycoord) 40 | functions.refresh_current_menu() 41 | self.father._refresh_ddlf() 42 | self.father.unblit() 43 | self.father.blit() 44 | self.father.update() 45 | if self.father.folders: 46 | self.father._clicked = "" 47 | inserted = self._path_list[-1] 48 | self.father._refresh_select(inserted) 49 | 50 | 51 | def _set_path_elements(self, ycoord=None): 52 | self.remove_all_elements() 53 | i = 0 54 | for s in self._path_list: 55 | e = Clickable(s) 56 | e.set_style("text") 57 | e.normal_params.params["font_size"] = style.PATH_FONT_SIZE 58 | e.press_params.params["font_size"] = style.PATH_FONT_SIZE 59 | e.finish() 60 | e.user_func = self._reaction_path 61 | e.user_params = {"n" : i} 62 | e.set_jailed(self.father) 63 | ## e._lock_jail = True #so that in the future jail will always stay self.father 64 | self.add_elements([e]) 65 | i += 1 66 | father = self 67 | if self.father.is_finished(): 68 | father = self.father 69 | fr = father.get_storer_rect() 70 | wtot = h_store(fr, self._elements, gap=0, xstart="auto", ycoord=ycoord) 71 | if wtot > fr.width: 72 | h_store(fr, self._elements, gap=0, ycoord=ycoord, 73 | xstart=fr.right - wtot-2) 74 | self._n = len(self._elements) 75 | -------------------------------------------------------------------------------- /elements/_hoverutils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/elements/_hoverutils/__init__.py -------------------------------------------------------------------------------- /elements/_inserterutils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/elements/_inserterutils/__init__.py -------------------------------------------------------------------------------- /elements/_inserterutils/_cursor.py: -------------------------------------------------------------------------------- 1 | from thorpy.elements.element import Element 2 | from thorpy.miscgui.reaction import Reaction 3 | from thorpy.miscgui.initializer import Initializer 4 | from thorpy.miscgui.state import State 5 | from thorpy.miscgui import constants, parameters, style, painterstyle 6 | 7 | class _Cursor(Element): 8 | 9 | def __init__(self, 10 | father, 11 | fact=None, 12 | thickness=None, 13 | color=None): 14 | fact = style.CURS_FACT if fact is None else fact 15 | thickness = style.CURS_THICK if thickness is None else thickness 16 | color = style.CURS_COLOR if color is None else color 17 | self.father = father 18 | self._fact = fact # should be 0 if img is used 19 | cursor_size = (thickness, self._fact*self.father._iwriter.get_zone().h) 20 | normal_painter = painterstyle.CURSOR_PAINTER(size=cursor_size, 21 | color=color) 22 | timed_painter = painterstyle.CURSOR_PAINTER( 23 | size=cursor_size, 24 | color=constants.TRANSPARENT) 25 | super(_Cursor, self).__init__(finish=False) 26 | self.normal_params.params["painter"] = normal_painter 27 | self.time_params = Initializer() 28 | self.time_params.params["painter"] = timed_painter 29 | reac_time = Reaction(constants.THORPY_EVENT, 30 | self._reaction_time, 31 | {"id":constants.EVENT_TIME}, 32 | reac_name=constants.REAC_TIME) 33 | self.add_reaction(reac_time) 34 | self.switch_time = parameters.CURSOR_INTERVAL # switch time in ms 35 | self._switch = 0 # time [ms] from last state switch 36 | self._activated = False 37 | self._original_blit = self.blit 38 | self.init_space = 0 39 | 40 | def finish(self): 41 | Element.finish(self) 42 | time_state = State(self.time_params.get_fusionner()) 43 | self.add_state(constants.REAC_TIME, time_state) 44 | self.set_init_pos() 45 | self.change_state(constants.REAC_TIME) 46 | 47 | def exit(self): 48 | self._activated = False 49 | self.change_state(constants.REAC_TIME) 50 | self.unblit() 51 | self.update() 52 | 53 | def set_topleft(self, pos, state=constants.STATE_NORMAL): 54 | left, top = pos 55 | w = self._states[state].fusionner.rect.width 56 | zone = self.father._iwriter.get_zone() 57 | if left + w > zone.right: 58 | left = zone.right - w 59 | Element.set_topleft(self, (left, top), state) 60 | 61 | def set_init_pos(self): 62 | """Set cursor to initial position""" 63 | pos = self.father._iwriter._get_cursor_pos() 64 | zone = self.father._iwriter.get_zone() 65 | x = pos[0] + self.init_space 66 | y = zone.y + (1. - self._fact) / 2 * zone.h 67 | self.set_topleft((x, y)) 68 | 69 | ## def switch_state(self): 70 | ## """Switch REAC_TIME and STATE_NORMAL states""" 71 | ## if self.current_state_key == constants.REAC_TIME: 72 | ## self.change_state(constants.STATE_NORMAL) 73 | ## self.solo_blit() 74 | ## else: 75 | ## self.change_state(constants.REAC_TIME) 76 | ## r = self.get_fus_rect() 77 | ## self.father.unblit(r) 78 | ## self.father.partial_blit(self, r) 79 | ## self.solo_update() 80 | 81 | def switch_state(self): 82 | """Switch REAC_TIME and STATE_NORMAL states""" 83 | if self.current_state_key == constants.REAC_TIME: 84 | self.change_state(constants.STATE_NORMAL) 85 | else: 86 | self.change_state(constants.REAC_TIME) 87 | self.father.unblit() 88 | self.father.blit() 89 | self.father.update() 90 | 91 | def _reaction_time(self, event): 92 | """Reaction to EVENT_TIME event""" 93 | if self._activated: 94 | self._switch += event.tick 95 | if self._switch > self.switch_time: # then must switch 96 | self._switch = 0 97 | self.switch_state() 98 | -------------------------------------------------------------------------------- /elements/_inserterutils/_insertwriter.py: -------------------------------------------------------------------------------- 1 | from thorpy.painting.writer import Writer 2 | from thorpy.elements.text import OneLineText 3 | from thorpy.miscgui import style 4 | 5 | 6 | class _InsertWriter(OneLineText): 7 | 8 | def __init__(self, text="", margin=None, writer=None,finish=True): 9 | margin = style.INSERTWRITER_MARGIN if margin is None else margin 10 | OneLineText.__init__(self, text,finish=False) 11 | self.margin = margin 12 | if not writer: 13 | self.writer = Writer() 14 | else: 15 | self.writer = writer 16 | if finish: 17 | self.finish() 18 | 19 | def get_zone(self): 20 | return self.father.get_clip() 21 | # return self.father.get_fus_rect() 22 | 23 | def _is_small_enough(self, word): 24 | w = self.current_state.fusionner.title._writer.get_width(word) 25 | if w + self.margin >= self.get_zone().width: 26 | return False 27 | return True 28 | 29 | def refresh_img(self): 30 | """Refresh self's text. Returns -1 if the text is too large.""" 31 | text = self.father._inserted 32 | txt_img = self.writer.get_imgs(text)[0] 33 | if txt_img.get_size()[0] + self.margin >= self.get_zone().width: 34 | return -1 35 | self.set_text(text) 36 | 37 | def _refresh_pos(self): 38 | zone = self.get_zone() 39 | y = (zone.height - self.writer.get_height()) / 2 40 | self.set_topleft((zone.x + self.margin, zone.y + y)) 41 | 42 | def _get_cursor_pos(self): 43 | text = self.father._inserted[0:self.father._cursor_index] 44 | w = self.current_state.fusionner.title._writer.get_width(text) 45 | zone = self.get_zone() 46 | curs_height = self.father.cursor.get_fus_size()[1] 47 | y = zone.y + (zone.h - curs_height) / 2 48 | return (zone.x + self.margin + w, y) 49 | -------------------------------------------------------------------------------- /elements/_makeuputils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/elements/_makeuputils/__init__.py -------------------------------------------------------------------------------- /elements/_makeuputils/_halo.py: -------------------------------------------------------------------------------- 1 | from pygame import Surface 2 | 3 | from thorpy.elements.element import Element 4 | from thorpy.painting import pilgraphics 5 | from thorpy.painting.painters.imageframe import ImageFrame 6 | from thorpy.miscgui import constants 7 | 8 | 9 | SHADOW_RADIUS = 10 10 | BLACK = 255 11 | ALPHA_FACTOR = 0.85 12 | DECAY_MODE = "linear" 13 | CAPTURE_STATE_STATIC = constants.STATE_NORMAL 14 | OFFSET = (0., 0.) 15 | 16 | class Halo(Element): 17 | def __init__(self, target, color, elements=None, normal_params=None): 18 | Element.__init__(self, "", elements, normal_params) 19 | self.link(target) 20 | self.shadow_radius = SHADOW_RADIUS 21 | self.black = BLACK 22 | self.alpha_factor = ALPHA_FACTOR 23 | self.decay_mode = DECAY_MODE 24 | self.capture_state = CAPTURE_STATE_STATIC 25 | self.offset = OFFSET 26 | self.color = color 27 | 28 | def link(self, target): 29 | self.target = target 30 | self.target.add_elements([self]) 31 | ## self.target.set_blit_before(self) 32 | self.target._halo = self 33 | 34 | def unlink(self): 35 | self.target.remove_elements([self]) 36 | self.target._halo = None 37 | self.target = None 38 | 39 | def _get_raw_shadow(self): 40 | target_img = self.target.get_image(self.capture_state) 41 | r = target_img.get_rect() 42 | #the shadow will be larger in order to make free space for fadeout. 43 | r.inflate_ip(2*self.shadow_radius, 2*self.shadow_radius) 44 | img = Surface(r.size) 45 | img.fill((255, 255, 255, 255)) 46 | img.blit(target_img, (self.shadow_radius, self.shadow_radius)) 47 | shadow = pilgraphics.get_shadow(img, 48 | radius=self.shadow_radius, 49 | black=self.black, 50 | alpha_factor=self.alpha_factor, 51 | decay_mode=self.decay_mode, 52 | color=self.color) 53 | return shadow 54 | 55 | def _get_shadow_painter(self): 56 | shadow = self._get_raw_shadow() 57 | return ImageFrame(shadow, alpha=-1) 58 | 59 | def _refresh_position(self): 60 | self.center(element=self.target) 61 | 62 | def finish(self): 63 | painter = self._get_shadow_painter() 64 | self.set_painter(painter) 65 | Element.finish(self) 66 | self._refresh_position() -------------------------------------------------------------------------------- /elements/_sliderutils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/elements/_sliderutils/__init__.py -------------------------------------------------------------------------------- /elements/_sliderutils/_shifters.py: -------------------------------------------------------------------------------- 1 | from pygame.mouse import get_pressed 2 | 3 | from thorpy.elements.clickable import Clickable 4 | from thorpy.miscgui.constants import STATE_PRESSED 5 | from thorpy.miscgui.reaction import Reaction 6 | from thorpy.miscgui import parameters, constants, style 7 | from thorpy.painting.graphics import blit_arrow_on 8 | 9 | 10 | class Shifter(Clickable): 11 | 12 | def __init__(self): 13 | Clickable.__init__(self) 14 | reac_rightclick = Reaction(parameters.BUTTON_UNPRESS_EVENT, 15 | self._reaction_rightpress, 16 | {"button": parameters.RIGHT_CLICK_BUTTON}, 17 | reac_name=constants.REAC_RIGHT_CLICK) 18 | self.add_reaction(reac_rightclick) 19 | self.normal_params.polite_set("painter size", (16, 16)) 20 | self.press_params.polite_set("painter size", (16, 16)) 21 | 22 | def _reaction_rightpress(self, event): 23 | pass 24 | 25 | def add_arrow(self, side): 26 | #first, get frame: 27 | normal_img = self.get_image(constants.STATE_NORMAL) 28 | press_img = self.get_image(constants.STATE_PRESSED) 29 | #then blit an arrow 30 | blit_arrow_on(style.ARROW_IMG, style.ARROW_IMG_COLORKEY, 31 | style.ARROW_IMG_COLORSOURCE, 32 | style.ARROW_COLOR, side, normal_img) 33 | blit_arrow_on(style.ARROW_IMG, style.ARROW_IMG_COLORKEY, 34 | style.ARROW_IMG_COLORSOURCE, 35 | style.ARROW_COLOR, side, press_img) 36 | #then set the new image (with the arrow) 37 | self.set_image(normal_img, constants.STATE_NORMAL, False) 38 | self.set_image(press_img, constants.STATE_PRESSED, False) 39 | #now we do the same for hovered images 40 | normal_hov = self._hover_imgs[constants.STATE_NORMAL] 41 | press_hov = self._hover_imgs[constants.STATE_PRESSED] 42 | blit_arrow_on(style.ARROW_IMG, style.ARROW_IMG_COLORKEY, 43 | style.ARROW_IMG_COLORSOURCE, 44 | style.COLOR_TXT_HOVER, side, normal_hov) 45 | blit_arrow_on(style.ARROW_IMG, style.ARROW_IMG_COLORKEY, 46 | style.ARROW_IMG_COLORSOURCE, 47 | style.COLOR_TXT_HOVER, side, press_hov) 48 | self.set_image(normal_hov, constants.STATE_NORMAL, hovered=True) 49 | self.set_image(press_hov, constants.STATE_PRESSED, hovered=True) 50 | 51 | 52 | class Plus(Shifter): 53 | 54 | def finish(self): 55 | Clickable.finish(self) 56 | self.add_arrow("top") 57 | 58 | def _reaction_rightpress(self, event): 59 | if self.collide(event.pos, self.current_state_key): 60 | self.father._drag_element.goto_start() 61 | 62 | def _reaction_time(self): 63 | if self.current_state_key == STATE_PRESSED: 64 | if get_pressed()[0]: 65 | self.father._drag_element.shift(parameters.CLICK_LIFT_REPEAT) 66 | 67 | 68 | 69 | class Minus(Shifter): 70 | 71 | def finish(self): 72 | Clickable.finish(self) 73 | self.add_arrow("bottom") 74 | 75 | def _reaction_rightpress(self, event): 76 | if self.collide(event.pos, self.current_state_key): 77 | self.father._drag_element.goto_end() 78 | 79 | def _reaction_time(self): 80 | if self.current_state_key == STATE_PRESSED: 81 | if get_pressed()[0]: 82 | self.father._drag_element.shift(-parameters.CLICK_LIFT_REPEAT) 83 | -------------------------------------------------------------------------------- /elements/background.py: -------------------------------------------------------------------------------- 1 | from thorpy.elements.element import Element 2 | from thorpy.painting.painters.imageframe import ImageFrame 3 | from thorpy.painting.painters.basicframe import BasicFrame 4 | from thorpy.miscgui import functions 5 | 6 | 7 | class Background(Element): 8 | """Background element for another element or menu.""" 9 | 10 | @staticmethod 11 | def make(color=None, image=None, elements=None, mode="scale to screen"): 12 | """Background element for another element or menu. 13 | : if not None, define the color for the background. 14 | : if not None, define the image of the background. 15 | : 16 | None : if an image is passed, its original size is kept. Otherwise, 17 | a (white by default) rect of the size of the screen is 18 | used as background image. 19 | 'scale to screen' : if an image is passed, it is scaled to fit 20 | screen. Otherwise, see behaviour for None. 21 | 'cut to screen' : if an image is passed, it is shrinked to fit 22 | the screen. Otherwise, use behaviour for None. 23 | """ 24 | background = Background(color, image, elements, mode=mode, finish=False) 25 | background.finish() 26 | return background 27 | 28 | def __init__(self, color=None, image=None, elements=None, 29 | normal_params=None, mode="scale to screen", finish=True): 30 | """Background element for another element or menu. 31 | : if not None, define the color for the background. 32 | : if not None, define the image of the background. 33 | : 34 | None : if an image is passed, its original size is kept. Otherwise, 35 | a (white by default) rect of the size of the screen is 36 | used as background image. 37 | 'scale to screen' : if an image is passed, it is scaled to fit 38 | screen. Otherwise, see behaviour for None. 39 | 'cut to screen' : if an image is passed, it is shrinked to fit 40 | the screen. Otherwise, use behaviour for None. 41 | """ 42 | super(Background, self).__init__("", elements, normal_params, finish=False) 43 | W, H = functions.get_screen_size() 44 | if image: 45 | painter = ImageFrame(image, mode=mode) 46 | else: 47 | if color: 48 | painter = BasicFrame((W, H), color) 49 | else: 50 | painter = BasicFrame((W, H), (255, 255, 255)) 51 | self.set_painter(painter) 52 | if finish: 53 | self.finish() -------------------------------------------------------------------------------- /elements/box.py: -------------------------------------------------------------------------------- 1 | from thorpy.elements.element import Element 2 | from thorpy.miscgui.storage import Storer, store 3 | from thorpy.miscgui import functions, style, painterstyle 4 | 5 | 6 | class Box(Element): 7 | """Box containing other elements.""" 8 | 9 | @staticmethod 10 | def make(elements, size=None): 11 | box = Box(elements=elements, size=size, finish=False) 12 | box.finish() 13 | return box 14 | 15 | def __init__(self, elements=None, normal_params=None, 16 | storer_params=None, size=None, put_lift=True, finish=True): 17 | """Box containing other elements. 18 | : the text of the box bar. If no text, no bar is added. 19 | : if not None, force the size of the box. Else the box 20 | automatically fit children.""" 21 | Element.__init__(self, "", elements, normal_params, finish=False) 22 | self.storer_params = storer_params 23 | if self.storer_params is None: 24 | self.storer_params = dict() 25 | self._size = size 26 | self._has_lift = False 27 | self._put_lift = put_lift 28 | painter = functions.obtain_valid_painter(painterstyle.BOX_PAINTER, 29 | pressed=True, 30 | size=size, 31 | radius_ext=style.BOX_RADIUS) 32 | self.set_painter(painter) 33 | if finish: 34 | self.finish() 35 | 36 | def add_lift(self, axis="vertical", type_="normal"): 37 | Element.add_lift(self, axis, type_) 38 | self._has_lift = True 39 | 40 | def store(self, size=None): 41 | """ 42 | size: 43 | 'auto' or None : autoset_framesize 44 | elif size : set_size and center. 45 | """ 46 | size = self._size if not size else size 47 | storer = Storer(self, **self.storer_params) 48 | if size and not size == "auto": 49 | self.set_size(size) 50 | storer.center() 51 | elif size == "auto" or size is None: 52 | storer.autoset_framesize() 53 | (x, y) = self.is_family_bigger() 54 | if y and not self._put_lift: 55 | self.add_lift("vertical") 56 | ## self._lift.active_wheel = True 57 | self._lift.active_wheel = False 58 | self.set_prison() 59 | 60 | def set_size(self, size, state=None, center_title=True, adapt_text=True, 61 | cut=None, margins=None, refresh_title=False): 62 | if margins is None: margins=style.MARGINS 63 | Element.set_size(self, size, state, center_title, adapt_text, cut, margins, 64 | refresh_title) 65 | if self._lift: 66 | self.remove_elements([self._lift]) 67 | self.refresh_lift() 68 | 69 | def finish(self): 70 | Element.finish(self) 71 | self.store() 72 | 73 | 74 | 75 | class BarBox(Element): 76 | 77 | def __init__(self, elements=None, normal_params=None, height=None): 78 | Element.__init__(self, "", elements, normal_params) 79 | h = max([e.get_storer_rect().height for e in self.get_elements()]) + 2 80 | store(self, mode="h", x=1, y=h/2, align="center") 81 | if self.father: 82 | w = self.father.get_storer_rect().width 83 | else: 84 | w = functions.get_screen_size()[0] 85 | size = (w, h) 86 | painter = functions.obtain_valid_painter(painterstyle.BOX_PAINTER, 87 | pressed=True, 88 | size=size, 89 | radius=style.BOX_RADIUS) 90 | self.set_painter(painter) 91 | 92 | def set_standard_style(self): 93 | from thorpy.painting.fusionner import _Fusionner 94 | for e in self.get_elements(): 95 | painter = functions.obtain_valid_painter(painterstyle.BASIC_PAINTER, 96 | size=e.get_fus_rect().size) 97 | fusionner = _Fusionner(painter, e.get_title()) 98 | e.set_image(fusionner.img) -------------------------------------------------------------------------------- /elements/clickable.py: -------------------------------------------------------------------------------- 1 | from pygame.event import post, Event 2 | 3 | from thorpy.elements.pressable import Pressable 4 | from thorpy.elements.hoverable import Hoverable 5 | from thorpy.miscgui.constants import STATE_NORMAL, STATE_PRESSED, EVENT_PRESS, EVENT_UNPRESS, THORPY_EVENT 6 | 7 | 8 | class Clickable(Pressable, Hoverable): 9 | """Clickable Element (Pressable and hoverable)""" 10 | 11 | def __init__(self, text="", elements=None, normal_params=None, 12 | press_params=None, finish=True): 13 | """Pressable and hoverable element. 14 | : the text of the element. 15 | """ 16 | super(Clickable, self).__init__(text, elements, normal_params, 17 | press_params,finish=False) 18 | self.normal_params.polite_set("states hover", 19 | list([STATE_NORMAL, STATE_PRESSED])) 20 | if finish: 21 | self.finish() 22 | 23 | def finish(self): 24 | Pressable.finish(self) 25 | self._set_hovered_states_auto() 26 | ## Hoverable.finish(self) 27 | 28 | def _remove_help(self): 29 | if self._help_element: 30 | self._help_element.unblit() 31 | self._help_element.update() 32 | self._help_element.set_recursive("visible", False) 33 | self._waited = 0 34 | 35 | def _press(self): 36 | state_ok = self.current_state == self._states[STATE_NORMAL] 37 | if state_ok: 38 | self.change_state(STATE_PRESSED) 39 | self._hover() 40 | ev_press = Event(THORPY_EVENT, id=EVENT_PRESS, el=self) 41 | post(ev_press) 42 | self._remove_help() 43 | 44 | def _unpress(self): 45 | self.change_state(STATE_NORMAL) 46 | ev_unpress = Event(THORPY_EVENT, id=EVENT_UNPRESS, el=self) 47 | post(ev_unpress) 48 | 49 | def _reaction_unpress(self, pygame_event): 50 | state_ok = self.current_state == self._states[STATE_PRESSED] 51 | if state_ok: 52 | self._unpress() 53 | if self.collide(pygame_event.pos, STATE_PRESSED): 54 | self._hover() 55 | self.run_user_func() 56 | else: 57 | self._unhover() 58 | 59 | def _reaction_unpress_key(self): 60 | state_ok = self.current_state == self._states[STATE_PRESSED] 61 | if state_ok: 62 | self._unpress() 63 | self._hover() 64 | self.run_user_func() 65 | -------------------------------------------------------------------------------- /elements/image.py: -------------------------------------------------------------------------------- 1 | from thorpy.elements.element import Element 2 | from thorpy.painting.painters.imageframe import ImageFrame 3 | from thorpy.painting.painters.basicframe import BasicFrame 4 | from thorpy.miscgui import style 5 | 6 | 7 | class Image(Element): 8 | """Image element.""" 9 | 10 | @staticmethod 11 | def make(path=None, color=None, colorkey=None): 12 | """Image element. 13 | : the path to the image. 14 | : if path is None, use this color instead of image. 15 | """ 16 | img = Image(path, color, colorkey=colorkey, finish=False) 17 | img.finish() 18 | return img 19 | 20 | def __init__(self, path=None, color=None, elements=None, normal_params=None, 21 | colorkey=None, finish=True): 22 | """Image element. 23 | : the path to the image. 24 | : if path is None, use this color instead of image. 25 | """ 26 | super(Image, self).__init__("", elements, normal_params, finish=False) 27 | if path: 28 | painter = ImageFrame(path, mode=None, colorkey=colorkey) 29 | else: 30 | if color: 31 | painter = BasicFrame(style.SIZE, color) 32 | else: 33 | raise Exception("You must specify either a path or a color") 34 | self.set_painter(painter) 35 | if finish: 36 | self.finish() 37 | 38 | def set_alpha(self, alpha): 39 | img = self.get_image() 40 | img.set_alpha(alpha) 41 | self.set_image(img) 42 | -------------------------------------------------------------------------------- /elements/keypressable.py: -------------------------------------------------------------------------------- 1 | from pygame.locals import KEYDOWN, KEYUP 2 | 3 | from thorpy.elements.pressable import Pressable 4 | from thorpy.miscgui.constants import STATE_NORMAL, STATE_PRESSED 5 | 6 | 7 | class KeyPressable(Pressable): 8 | """Keyboard pressable element""" 9 | 10 | def __init__(self, key, text="", elements=None, normal_params=None, 11 | press_params=None, type_=KEYDOWN, untyp=KEYUP, finish=True): 12 | """Keyboard pressable element 13 | : the pygame keyboard key for press event.""" 14 | super(KeyPressable, self).__init__(text, elements, normal_params, 15 | press_params, finish=False) 16 | self._set_press_reaction(type_, args=dict({"key": key})) 17 | self._set_unpress_reaction(untyp, args=dict({"key": key})) 18 | if finish: 19 | self.finish() 20 | 21 | def _reaction_press(self, event): 22 | if self.current_state_key == STATE_NORMAL: 23 | Pressable._press(self) 24 | 25 | def _reaction_unpress(self, event): 26 | if self.current_state_key == STATE_PRESSED: 27 | Pressable._unpress(self) 28 | -------------------------------------------------------------------------------- /elements/keytogglable.py: -------------------------------------------------------------------------------- 1 | from pygame.locals import KEYDOWN 2 | 3 | from thorpy.elements.pressable import Pressable 4 | from thorpy.miscgui.constants import STATE_NORMAL, STATE_PRESSED, REAC_UNPRESS 5 | 6 | 7 | class KeyTogglable(Pressable): 8 | """Keyboard togglable element""" 9 | 10 | def __init__(self, key, text="", elements=None, normal_params=None, 11 | press_params=None, type_=KEYDOWN, finish=True): 12 | """Keyboard togglable element 13 | : the pygame keyboard key for press event.""" 14 | super(KeyTogglable, self).__init__(text, elements, normal_params, 15 | press_params, finish=False) 16 | self._set_press_reaction(type_, args=dict({"key": key})) 17 | ## self.set_key(key, type_) 18 | self._reactions.pop(REAC_UNPRESS) 19 | if finish: 20 | self.finish() 21 | 22 | def set_key(self, key, event_type=KEYDOWN): 23 | self._set_press_reaction(event_type, args=dict({"key": key})) 24 | 25 | def _reaction_press(self, event): 26 | if self.current_state_key == STATE_NORMAL: 27 | Pressable._press(self) 28 | elif self.current_state_key == STATE_PRESSED: 29 | Pressable._unpress(self) 30 | self.run_user_func() 31 | -------------------------------------------------------------------------------- /elements/launchers/__init__.py: -------------------------------------------------------------------------------- 1 | ##import browserlightlauncher 2 | ##import launcher 3 | ##import dropdownlistlauncher 4 | ##import boxlauncher 5 | -------------------------------------------------------------------------------- /elements/lifebar.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | import thorpy, pygame 3 | 4 | class LifeBar(thorpy.Element): 5 | 6 | @staticmethod 7 | def make(text, color=(255,165,0), text_color=(0,0,0), 8 | size=(200,30), font_size=None, type_="h"): 9 | return LifeBar(text,color,text_color,size,font_size,type_) 10 | 11 | def __init__(self, text, color=(255,165,0), text_color=(0,0,0), 12 | size=(200,30), font_size=None, type_="h"): 13 | thorpy.Element.__init__(self) 14 | painter = thorpy.painterstyle.ClassicFrame(size, 15 | color=thorpy.style.DEF_COLOR, 16 | pressed=True) 17 | self.set_painter(painter) 18 | self.finish() 19 | if type_ == "v": 20 | self.set_life = self.set_life_v 21 | self.get_life = self.get_life_v 22 | else: 23 | self.set_life = self.set_life_h 24 | self.get_life = self.get_life_h 25 | # 26 | self.life_text = thorpy.make_text(text,font_color=text_color,font_size=font_size) 27 | self.life_text.center(element=self) 28 | self.life_color = color 29 | self.add_elements([self.life_text]) 30 | self.life_width = size[0]-2 31 | self.life_height = size[1]-2 32 | self.life_rect = pygame.Rect(1,1, self.life_width,self.life_height) 33 | 34 | def set_text(self, text): 35 | self.life_text.set_text(text) 36 | self.life_text.center(element=self) 37 | 38 | def blit(self): 39 | """Recursive blit""" 40 | self._clip_screen() 41 | for e in self._blit_before: 42 | e.blit() 43 | if self.visible: 44 | self.solo_blit() 45 | pygame.draw.rect(self.surface, self.life_color, self.life_rect) 46 | for e in self._blit_after: 47 | e.blit() 48 | self._unclip_screen() 49 | 50 | def move(self,shift): 51 | thorpy.Element.move(self,shift) 52 | self.life_rect.move_ip(shift) 53 | 54 | def set_life_h(self,life): 55 | self.life_rect.width = int(life*self.life_width) 56 | 57 | def set_life_v(self,life): 58 | tmp = self.life_rect.bottom 59 | self.life_rect.height = int(life*self.life_height) 60 | self.life_rect.bottom = tmp 61 | 62 | def get_life_h(self,life): 63 | return self.life_rect.width / float(self.life_width) 64 | 65 | def get_life_v(self,life): 66 | return self.life_rect.height / float(self.life_height) 67 | 68 | class SkillBar(LifeBar): 69 | 70 | @staticmethod 71 | def make(text, m, M, color=(255,165,0), text_color=(0,0,0), 72 | size=(200,30), font_size=None): 73 | return SkillBar(text,m,M,color,text_color,size,font_size) 74 | 75 | def __init__(self, text, m, M, color=(255,165,0), text_color=(0,0,0), 76 | size=(200,30), font_size=None): 77 | LifeBar.__init__(self,text,color,text_color,size,font_size) 78 | self.m = m 79 | self.M = M 80 | 81 | def set_skill(self, value): 82 | self.set_life(value) 83 | 84 | def set_life(self,life): 85 | value = (life - self.m)/(self.M-self.m) 86 | LifeBar.set_life(self, value) 87 | -------------------------------------------------------------------------------- /elements/line.py: -------------------------------------------------------------------------------- 1 | from thorpy.elements.element import Element 2 | from thorpy.painting.painters.classicframe import ClassicFrame 3 | 4 | class Line(Element): 5 | """Vertical or horizontal graphical separation Line.""" 6 | 7 | @staticmethod 8 | def make(size, type_="h", color=None, pressed=True): 9 | """Vertical or horizontal graphical separation Line. 10 | : the size in pixel (single int value). 11 | : either 'horizontal', 'h' or 'vertical', 'v'. 12 | : a 3 or 4-tuple specifying the color. 13 | : if True, the line looks pressed. 14 | """ 15 | line = Line(size, type_, color, pressed, finish=False) 16 | line.finish() 17 | return line 18 | 19 | def __init__(self, size, type_, color=None, pressed=True, finish=True): 20 | """Vertical or horizontal graphical separation Line. 21 | : the size in pixel (single int value). 22 | : either 'horizontal', 'h' or 'vertical', 'v'. 23 | : a 3 or 4-tuple specifying the color. 24 | : if True, the line looks pressed. 25 | """ 26 | Element.__init__(self, finish=False) 27 | self.size = size 28 | self.type = type_ 29 | if type_ == "horizontal" or type_ == "h": 30 | size = (size, 2) 31 | elif type_ == "vertical" or type_ == "v": 32 | size = (2, size) 33 | painter = ClassicFrame(size, color, pressed) 34 | self.set_painter(painter) 35 | if finish: 36 | self.finish() 37 | 38 | def copy(self): 39 | return Line(self.size, self.type) -------------------------------------------------------------------------------- /elements/paramsetter.py: -------------------------------------------------------------------------------- 1 | from thorpy.elements.box import Box 2 | from thorpy.miscgui import style 3 | from thorpy.elements.launchers.dropdownlistlauncher import DropDownListLauncher 4 | 5 | 6 | class ParamSetter(Box): 7 | """Put automatically defined elements in a box, in order to set variables. 8 | """ 9 | 10 | @staticmethod 11 | def make(varsets, elements=None, size=None): 12 | ps = ParamSetter(varsets, elements, size=size, finish=False) 13 | ps.finish() 14 | return ps 15 | 16 | def __init__(self, 17 | varsets, 18 | elements=None, 19 | normal_params=None, 20 | size=None, 21 | bar=None, 22 | file_width=None, 23 | finish=True): 24 | if file_width is None: file_width = style.FILE_WIDTH 25 | ## box_size = style.BOX_SIZE if box_size is None else box_size 26 | self.scale_list = [DropDownListLauncher] #!paramsetter 27 | self.varsets = varsets 28 | if not isinstance(self.varsets, list): 29 | self.varsets = [self.varsets] 30 | self.handlers = self.get_handlers() #below, will deny them so they know self 31 | elements = [] if elements is None else elements 32 | elements += self.handlers.values() 33 | Box.__init__(self, elements=elements, normal_params=normal_params, 34 | size=size, finish=False) 35 | if finish: 36 | self.finish() 37 | 38 | 39 | 40 | def get_handlers(self): 41 | """Returns a dictionnary of pairs (i, varname) containing elements.""" 42 | handlers = {} 43 | for (i, v) in enumerate(self.varsets): 44 | v_handlers = v.get_handlers() 45 | for (varname, handler) in iter(v_handlers.items()): 46 | handler_element, variable = handler 47 | handler_element.finish() 48 | if handler_element.__class__ in self.scale_list: 49 | handler_element.scale_to_title() 50 | if variable.help_text: 51 | add_basic_help(handler_element, variable.help_text) 52 | handlers[(i, varname)] = handler_element 53 | return handlers 54 | 55 | 56 | ## def reinit_handlers(self): 57 | ## """Returns a dictionnary of pairs (i, varname) containing elements.""" 58 | ## handlers = {} 59 | ## for (i, v) in enumerate(self.varsets): 60 | ## v_handlers = v.get_handlers() 61 | ## for (varname, handler) in iter(v_handlers.items()): 62 | ## handler_element, variable = handler 63 | ## handler_element.finish() 64 | ## if variable.help_text: 65 | ## add_basic_help(handler_element, variable.help_text) 66 | ## handlers[(i, varname)] = handler_element 67 | ## for key in self.handlers: 68 | ## old_handler = self.handlers[key] 69 | ## new_handler = handlers[key] 70 | ## self.replace_element(old_handler, new_handler) 71 | ## self.handlers = handlers 72 | 73 | def reinit_handlers(self): 74 | """Returns a dictionnary of pairs (i, varname) containing elements.""" 75 | for i, varname in self.handlers: 76 | value = self.varsets[i].get_value(varname) 77 | print(self.handlers[(i,varname)], value) 78 | self.handlers[(i,varname)].set_value(value) 79 | 80 | 81 | def save(self): 82 | for (varset, varname), handler in iter(self.handlers.items()): 83 | # si varset 84 | self.varsets[varset].set_value(varname, handler.get_value()) 85 | # sinon si link 86 | # sinon si fonction 87 | 88 | ## def reinit(self): 89 | ## for (varset, varname), handler in iter(self.handlers.items()): 90 | ## self.handlers.remove() 91 | ## # si varset 92 | ## self.varsets[varset].set_value(varname, handler.get_value()) 93 | -------------------------------------------------------------------------------- /elements/text.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | from thorpy.elements.element import Element 4 | from thorpy.miscgui.constants import STATE_NORMAL 5 | 6 | 7 | class OneLineText(Element): 8 | """Simpe text on only one line.""" 9 | 10 | def __init__(self, text="", elements=None, normal_params=None, finish=True): 11 | """Simpe text on only one line.""" 12 | Element.__init__(self, text, elements, normal_params, finish=False) 13 | if finish: 14 | self.finish() 15 | 16 | def finish(self): 17 | self.set_style("text") 18 | Element.finish(self) 19 | 20 | 21 | class MultilineText(Element): 22 | """Simple text on multiple lines.""" 23 | 24 | def __init__(self, text="", size=None, elements=None, normal_params=None, 25 | finish=True): 26 | """Simple text on multiple lines. 27 | : the size of the area on which the text is displayed. 28 | """ 29 | Element.__init__(self, text, elements, normal_params,finish=False) 30 | self._size = size 31 | self.visible = False 32 | if finish: 33 | self.finish() 34 | 35 | def finish(self): 36 | Element.finish(self) 37 | if not self._size: 38 | self._size = self.get_fus_rect() 39 | self.set_size(self._size) 40 | for line in self.get_lines(STATE_NORMAL): 41 | e = OneLineText(line) 42 | e.finish() 43 | e.set_writer(self.current_state.fusionner.title._writer) 44 | self.add_elements([e]) 45 | self.format_txt() 46 | 47 | def build_elements(self): 48 | for e in self._elements: 49 | e.father = None 50 | self._elements = [] 51 | self._blit_before = [] 52 | self._blit_after = [] 53 | self.set_size(self._size) 54 | for line in self.get_lines(STATE_NORMAL): 55 | e = OneLineText(line) 56 | e.finish() 57 | e.set_writer(self.current_state.fusionner.title._writer) 58 | self.add_elements([e]) 59 | self.format_txt() 60 | 61 | def format_txt(self): 62 | title = self._states[STATE_NORMAL].fusionner.title 63 | (x, y) = title._pos 64 | r = title.get_rect() 65 | for i in self._elements: 66 | (w, h) = i.get_fus_size() 67 | if title._align is "left": 68 | x = title._pos[0] 69 | elif title._align is "center": 70 | x = (r.width - w) // 2 71 | elif title._align is "right": 72 | x = r.width - w 73 | i.set_topleft((x, y)) 74 | y += title._space + h 75 | 76 | def set_font_color(self, color, state=None, center_title=True): 77 | """set font color for a given state""" 78 | Element.set_font_color(self, color, state, center_title) 79 | self.build_elements() 80 | 81 | def set_font_size(self, size, state=None, center_title=True): 82 | """set font color for a given state""" 83 | Element.set_font_size(self, size, state, center_title) 84 | self.build_elements() 85 | 86 | def set_font(self, fontname, state=None, center_title=True): 87 | """set font for a given state""" 88 | Element.set_font(self, fontname, state, center_title) 89 | self.set_hovered_states(self._states_hover) 90 | 91 | def set_font_effects(self, biu, state=None, center=True, preserve=False): 92 | """biu = tuple : (bold, italic, underline)""" 93 | Element.set_font_effects(self, biu, state, center, preserve) 94 | self.build_elements() 95 | -------------------------------------------------------------------------------- /elements/togglable.py: -------------------------------------------------------------------------------- 1 | from pygame.event import Event, post 2 | 3 | from thorpy.elements.clickable import Clickable 4 | from thorpy.miscgui import constants 5 | 6 | 7 | class Togglable(Clickable): 8 | """Mouse-togglable element""" 9 | 10 | def __init__(self, text="", elements=None, normal_params=None, 11 | press_params=None): 12 | super(Togglable, self).__init__(text, elements, normal_params, 13 | press_params) 14 | self._count = 0 15 | self.toggled = False 16 | 17 | def _reaction_press(self, event): 18 | if self._count < 1: 19 | tag = constants.STATE_NORMAL 20 | else: 21 | tag = constants.STATE_PRESSED 22 | if self.collide(event.pos, tag): 23 | self._press() 24 | 25 | def _press(self): 26 | Clickable._press(self) 27 | self._count += 1 28 | if not self.toggled: 29 | ev_tog = Event(constants.THORPY_EVENT, id=constants.EVENT_TOGGLE, 30 | el=self) 31 | post(ev_tog) 32 | self.toggled = True 33 | ## print("posted t") 34 | 35 | def _force_unpress(self): 36 | self._count = 0 37 | Clickable._unpress(self) 38 | ## self._unhover_noblit() 39 | ev_untog = Event(constants.THORPY_EVENT, 40 | id=constants.EVENT_UNTOGGLE, el=self) 41 | post(ev_untog) 42 | self.toggled = False 43 | ## print("posted u") 44 | 45 | def _unpress(self): 46 | if self._count == 2: 47 | self._force_unpress() 48 | -------------------------------------------------------------------------------- /examples/advancedmenus.py: -------------------------------------------------------------------------------- 1 | """Script showing several ways to launch things. 2 | 3 | Note: never call set_launcher(launching, launched) if can be launched 4 | by another element and is NOT in the same menu. 5 | """ 6 | import thorpy, pygame 7 | 8 | application = thorpy.Application((500,500), "Advanced menus") 9 | 10 | # ****************** First launcher : button 1 ****************** 11 | #This launcher launches a simple button 12 | my_element = thorpy.make_button("I am a useless button\nClick outside to quit.") 13 | button1 = thorpy.make_button("Launcher 1") 14 | #we set click_quit=True below, because we did not provide a "ok" and/or "cancel" 15 | #button to the user. Element disappears When user clicks outside it. 16 | thorpy.set_launcher(button1, my_element, click_quit=True) 17 | 18 | # ****************** Second launcher : button 2 ****************** 19 | #here the element to be launched is a box with ok and cancel buttons + custom 20 | #elements. We can also use make_ok_box, with only 1 text. 21 | #Note that DONE_EVENT and CANCEL_EVENT are posted accordingly at unlaunch. 22 | box = thorpy.make_ok_cancel_box([thorpy.make_button(str(i)) for i in range(8)], 23 | ok_text="Ok", cancel_text="Cancel") 24 | button2 = thorpy.make_button("Launcher 2") 25 | thorpy.set_launcher(button2, box) 26 | 27 | # ****************** Third launcher : button 3 ****************** 28 | #This launcher launches a box, set it green, and changes screen color when 29 | #unlaunched. 30 | button3 = thorpy.make_button("Launcher 3") 31 | other_box = thorpy.make_ok_box([thorpy.make_text("Color is gonna change...")]) 32 | my_launcher = thorpy.set_launcher(button3, other_box)#this time get the launcher 33 | #we specify some custom operations that have to be done before/after launching: 34 | def my_func_before(): 35 | my_launcher.launched.set_main_color((0,255,0)) #change launched box color 36 | my_launcher.default_func_before() #default stuff 37 | def my_func_after(): 38 | background.set_main_color((0,100,100)) #change background color 39 | my_launcher.default_func_after() #default stuff 40 | 41 | my_launcher.func_before = my_func_before 42 | my_launcher.func_after = my_func_after 43 | 44 | # ****************** Fourth launcher : event ****************** 45 | #This launcher is not linked to a ThorPy element, but instead user can activate 46 | #it by pressing SPACE 47 | unlaunch_button = thorpy.make_ok_box([thorpy.make_text("Ready to unlaunch?")]) 48 | unlaunch_button.stick_to("screen", "top", "top") 49 | invisible_launcher = thorpy.get_launcher(unlaunch_button, autocenter=False) 50 | # set focus to False for non-blocking behaviour: 51 | ##invisible_launcher.focus = False 52 | #this reaction will be added to the background: 53 | reac = thorpy.ConstantReaction(pygame.KEYDOWN, invisible_launcher.launch, 54 | {"key":pygame.K_SPACE}) 55 | #add a text so user knows what to do 56 | text4 = thorpy.make_text("Press space to launch invisible_launcher", 15) 57 | 58 | 59 | background = thorpy.Background(elements=[text4, button1, button2, button3]) 60 | background.add_reaction(reac) 61 | thorpy.store(background) 62 | 63 | menu = thorpy.Menu(background) 64 | menu.play() 65 | 66 | application.quit() -------------------------------------------------------------------------------- /examples/alerts.py: -------------------------------------------------------------------------------- 1 | """Show how to launch alerts. 2 types of alerts are available: blocking and 2 | non-blocking. Both are presented below. 3 | """ 4 | import thorpy 5 | 6 | def my_alert_1(): 7 | thorpy.launch_nonblocking_alert(title="This is a non-blocking alert!", 8 | text="This is the text..", 9 | ok_text="Ok, I've read", 10 | font_size=12, 11 | font_color=(255,0,0)) 12 | print("Proof that it is non-blocking : this sentence is printing at exit!") 13 | 14 | def my_alert_2(): 15 | thorpy.launch_blocking_alert(title="This is a blocking alert!", 16 | text="This is the text of the alert...", 17 | parent=background) #for auto-unblitting 18 | print("This sentence will print only after you clicked ok") 19 | 20 | def my_launch(): 21 | some_element = thorpy.make_text("My text...", font_size=18) 22 | another_element = thorpy.make_button("Quit") 23 | box = thorpy.Box.make([some_element, another_element]) 24 | thorpy.set_as_done_button(another_element,box)#could be set_as_cancel_button 25 | thorpy.launch_nonblocking(box) #could also use thorpy.launch_blocking... 26 | 27 | application = thorpy.Application((500,500), "Launching alerts") 28 | 29 | button1 = thorpy.make_button("Non-blocking alert", func=my_alert_1) 30 | button2 = thorpy.make_button("Blocking alert", func=my_alert_2) 31 | button3 = thorpy.make_button("Launch element", func=my_launch) 32 | 33 | background = thorpy.Background(elements=[button1,button2,button3]) 34 | thorpy.store(background, x=10, align="left") # put the menu on the left 35 | 36 | menu = thorpy.Menu(background) 37 | menu.play() 38 | 39 | application.quit() -------------------------------------------------------------------------------- /examples/animatedGif.py: -------------------------------------------------------------------------------- 1 | """Example from www.thorpy.org/examples.html""" 2 | import thorpy 3 | 4 | application = thorpy.Application((800, 600), "Example of animated gif") 5 | 6 | gif_element = thorpy.AnimatedGif("../documentation/examples/myGif.gif") 7 | gif_element.center() 8 | 9 | menu = thorpy.Menu(gif_element) 10 | menu.play() 11 | 12 | application.quit() 13 | 14 | """ 15 | Other parameters to pass to the constructor of AnimatedGif: 16 | : the path to the image. 17 | : if path is None, use this color instead of image. 18 | : increase this parameter to lower the gif speed. 19 | : number of times the gif is played 20 | """ 21 | -------------------------------------------------------------------------------- /examples/basicstyling.py: -------------------------------------------------------------------------------- 1 | import thorpy 2 | 3 | application = thorpy.Application((700, 700), "Style examples") 4 | 5 | buttons = [thorpy.make_button("button"+str(i)) for i in range(8)] 6 | 7 | buttons[0].set_main_color((0,255,0)) #slow method, to be used outside of a loop 8 | buttons[1].set_main_color((0,255,0,100)) #with alpha value this time 9 | buttons[2].set_font_color((255,255,0)) #slow method, to be used outside of a loop 10 | buttons[3].set_font_color_hover((0, 255, 0)) #... 11 | buttons[4].set_font("century") #may not work on your computer 12 | buttons[5].set_font_size(18);buttons[5].scale_to_title() 13 | buttons[6].set_size((100,100)) 14 | buttons[7].set_text("First line\nSecond line") 15 | buttons[7].scale_to_title() 16 | 17 | #use predefined theme 18 | for theme_name in ["classic", "round", "human", "simple", "windows10"]: 19 | thorpy.set_theme(theme_name) 20 | button = thorpy.make_button("Theme name : " + theme_name) 21 | button.scale_to_title() 22 | buttons.append(button) 23 | thorpy.theme.set_default_theme_as_current() 24 | 25 | #A customized style 26 | a_round_button = thorpy.Clickable("Custom round Button") 27 | painter = thorpy.painters.optionnal.human.Human(size=(200,30), 28 | radius_ext=0.5, 29 | radius_int=0.4, 30 | border_color=(0,0,255), 31 | color=(100,100,255)) 32 | a_round_button.set_painter(painter) 33 | a_round_button.finish() 34 | buttons.append(a_round_button) 35 | 36 | #Another customized style 37 | #set default painter as ClassicFrame (same as used in theme 'classic') 38 | thorpy.painterstyle.DEF_PAINTER = thorpy.painters.classicframe.ClassicFrame 39 | thorpy.style.MARGINS = (50, 2) #set default margins 40 | thorpy.style.COLOR_TXT_HOVER = (255,0,0) 41 | button = thorpy.make_button("Another custom button") 42 | buttons.append(button) 43 | 44 | #text button 45 | title = thorpy.make_text("Text only", font_size=22, font_color=(255,0,0)) 46 | title.stick_to("screen", "top", "top") 47 | 48 | #To add shadows to a button, see effects.py in the examples folder. 49 | 50 | background = thorpy.Background(image=thorpy.style.EXAMPLE_IMG, elements=buttons) 51 | thorpy.store(background) 52 | 53 | menu = thorpy.Menu([background, title]) 54 | menu.play() 55 | 56 | application.quit() -------------------------------------------------------------------------------- /examples/coupling.py: -------------------------------------------------------------------------------- 1 | #ThorPy real life tutorial : full final code 2 | import thorpy 3 | 4 | def refresh_sliders(event, drag, sx, sy): 5 | if event.el == drag: 6 | pos_drag = drag.get_rect().topleft 7 | sx.unblit_and_reblit_func(sx.set_value, value=pos_drag[0]) 8 | sy.unblit_and_reblit_func(sy.set_value, value=pos_drag[1]) 9 | 10 | def refresh_drag(event, drag, sx, sy): 11 | if event.el == sx or event.el == sy: 12 | drag.unblit_and_reblit_func(drag.set_topleft, 13 | pos=(sx.get_value(), sy.get_value())) 14 | 15 | def run_application(): 16 | W, H = 300, 300 17 | application = thorpy.Application(size=(W,H), caption="Real life example") 18 | 19 | draggable = thorpy.Draggable("Drag me") 20 | sx = thorpy.SliderX(length=100, limvals=(0, W), text="X:", type_=int) 21 | sy = thorpy.SliderX(length=100, limvals=(0, H), text="Y:", type_=int) 22 | 23 | background = thorpy.Background(color=(200,255,255), 24 | elements=[draggable, sx, sy]) 25 | thorpy.store(background, [sx, sy]) 26 | 27 | reaction1 = thorpy.Reaction(reacts_to=thorpy.constants.THORPY_EVENT, 28 | reac_func=refresh_drag, 29 | event_args={"id":thorpy.constants.EVENT_SLIDE}, 30 | params={"drag":draggable, "sx":sx, "sy":sy}, 31 | reac_name="my reaction to slide event") 32 | 33 | reaction2 = thorpy.Reaction(reacts_to=thorpy.constants.THORPY_EVENT, 34 | reac_func=refresh_sliders, 35 | event_args={"id":thorpy.constants.EVENT_DRAG}, 36 | params={"drag":draggable, "sx":sx, "sy":sy}, 37 | reac_name="my reaction to drag event") 38 | 39 | background.add_reaction(reaction1) 40 | background.add_reaction(reaction2) 41 | 42 | menu = thorpy.Menu(background) #create a menu for auto events handling 43 | menu.play() #launch the menu 44 | application.quit() 45 | 46 | run_application() -------------------------------------------------------------------------------- /examples/design.py: -------------------------------------------------------------------------------- 1 | import thorpy, pygame 2 | 3 | application = thorpy.Application((300, 300)) 4 | 5 | class MyPainter(thorpy.painters.painter.Painter): 6 | 7 | def __init__(self,c1, c2, c3, size=None, clip=None, pressed=False, 8 | hovered=False,): 9 | super(MyPainter, self).__init__(size, clip, pressed, hovered) 10 | self.c1 = c1 11 | self.c2 = c2 12 | self.c3 = c3 13 | 14 | def get_surface(self): 15 | #transparent surface so that all that is not drawn is invisible 16 | surface = pygame.Surface(self.size, flags=pygame.SRCALPHA).convert_alpha() 17 | rect_corner = pygame.Rect(0, 0, self.size[0]//6, self.size[1]//6) 18 | rect_body = surface.get_rect().inflate((-5,-5)) 19 | color_corner = self.c1 #this color will change accordin to the state 20 | if self.pressed: 21 | color_corner = self.c3 22 | #draw the four corners: 23 | if not self.hovered: 24 | for pos in [rect_body.topleft, rect_body.bottomleft, 25 | rect_body.topright, rect_body.bottomright]: 26 | rect_corner.center = pos 27 | pygame.draw.rect(surface, color_corner, rect_corner) 28 | pygame.draw.rect(surface, self.c2, rect_body) #draw body 29 | #draw body border: 30 | pygame.draw.rect(surface, self.c3, rect_body.inflate((-5,-5))) 31 | #redraw corner rects if hovered 32 | if self.hovered: 33 | for pos in [rect_body.topleft, rect_body.bottomleft, 34 | rect_body.topright, rect_body.bottomright]: 35 | rect_corner.center = pos 36 | pygame.draw.rect(surface, color_corner, rect_corner) 37 | surface.set_clip(self.clip) #don't forget to set clip 38 | return surface 39 | 40 | my_painter = MyPainter((255,0,0), (0,255,0), (200,200,255)) 41 | my_button = thorpy.Clickable("My Button") #don't use 'make' ! 42 | my_button.set_painter(my_painter) 43 | my_button.finish() #don't forget to call 'finish() 44 | my_button.center() 45 | 46 | background = thorpy.Background.make((255,255,255), elements=[my_button]) 47 | background.finish() 48 | 49 | menu = thorpy.Menu(background) 50 | menu.play() 51 | 52 | application.quit() -------------------------------------------------------------------------------- /examples/guessthenumber.py: -------------------------------------------------------------------------------- 1 | #ThorPy minigame tutorial. Guess the number : start menu 2 | import thorpy 3 | import _mygame as mygame 4 | 5 | def launch_game(): #launch the game using parameters from varset 6 | global varset, e_background 7 | game = mygame.MyGame(player_name=varset.get_value("player name"), 8 | min_val=varset.get_value("minval"), 9 | max_val=varset.get_value("maxval"), 10 | trials=varset.get_value("trials")) 11 | game.launch_game() 12 | game.e_background.unblit() #unblit the game when finished 13 | game.e_background.update() 14 | e_background.unblit_and_reblit() #reblit the start menu 15 | 16 | 17 | application = thorpy.Application(size=(600, 400), caption="Guess the number") 18 | 19 | thorpy.set_theme("human") 20 | 21 | e_title = thorpy.make_text("My Minigame", font_size=20, font_color=(0,0,150)) 22 | e_title.center() #center the title on the screen 23 | e_title.set_topleft((None, 10)) #set the y-coord at 10 24 | 25 | e_play = thorpy.make_button("Play!", func=launch_game) #launch the game 26 | 27 | varset = thorpy.VarSet() #here we will declare options that user can set 28 | varset.add("trials", value=5, text="Trials:", limits=(1, 20)) 29 | varset.add("minval", value=0, text="Min value:", limits=(0, 100)) 30 | varset.add("maxval", value=100, text="Max value:", limits=(0, 100)) 31 | varset.add("player name", value="Jack", text="Player name:") 32 | e_options = thorpy.ParamSetterLauncher.make([varset], "Options", "Options") 33 | 34 | e_quit = thorpy.make_button("Quit", func=thorpy.functions.quit_menu_func) 35 | 36 | e_background = thorpy.Background(color=(200, 200, 255), 37 | elements=[e_title, e_play, e_options, e_quit]) 38 | thorpy.store(e_background, [e_play, e_options, e_quit]) 39 | 40 | menu = thorpy.Menu(e_background) 41 | menu.play() 42 | 43 | application.quit() -------------------------------------------------------------------------------- /examples/imagebuttons.py: -------------------------------------------------------------------------------- 1 | """Show how to use image to make buttons. Here 2 buttons are created.""" 2 | 3 | import thorpy, pygame 4 | 5 | application = thorpy.Application((500,500), "Image buttons") 6 | 7 | root = "../documentation/examples/" 8 | normal, pressed, hover = "normal.png", "pressed.png", "hover.png" 9 | 10 | button1 = thorpy.make_image_button(root+normal, root+pressed, root+hover, 11 | alpha=255, #opaque 12 | colorkey=(255,255,255)) #white=transparent 13 | 14 | #this time a very simple button, with a text (only 1 image) 15 | button2 = thorpy.make_image_button(root+hover, colorkey=False, text="Hello") 16 | 17 | 18 | background = thorpy.Background(image=thorpy.style.EXAMPLE_IMG, 19 | elements=[button1, button2]) 20 | thorpy.store(background) 21 | 22 | menu = thorpy.Menu(background) 23 | menu.play() 24 | 25 | application.quit() -------------------------------------------------------------------------------- /examples/lifeBar.py: -------------------------------------------------------------------------------- 1 | """Example from www.thorpy.org/examples.html""" 2 | import thorpy 3 | 4 | application = thorpy.Application((800, 600), "ThorPy Overview") 5 | 6 | bar = thorpy.LifeBar("Remaining time", 7 | color=(255,165,0), 8 | text_color=(0,0,0), 9 | size=(200,30), 10 | font_size=None, #keep default one 11 | type_="h") #h or v 12 | bar.center() 13 | 14 | counter = 0 15 | def event_time(): 16 | global counter 17 | if counter%4 == 0: 18 | life = min(1.,counter/500.) 19 | bar.set_life(life) 20 | bar.set_text(str(counter)) 21 | bar.unblit_and_reblit() 22 | if counter < 500: 23 | counter += 1 24 | 25 | bar.add_reaction(thorpy.ConstantReaction(thorpy.THORPY_EVENT, 26 | event_time, 27 | {"id":thorpy.constants.EVENT_TIME})) 28 | 29 | menu = thorpy.Menu(bar) 30 | menu.play() 31 | 32 | application.quit() 33 | -------------------------------------------------------------------------------- /examples/overview.py: -------------------------------------------------------------------------------- 1 | import thorpy 2 | 3 | application = thorpy.Application((800, 600), "ThorPy Overview") 4 | 5 | text = thorpy.make_text("Some text", 12, (0,0,255)) 6 | line = thorpy.Line(200, "h") #horizontal line of width = 200px 7 | 8 | element = thorpy.Element("Element") 9 | thorpy.makeup.add_basic_help(element,"Element:\nMost simple graphical element.") 10 | 11 | clickable = thorpy.Clickable("Clickable") 12 | thorpy.makeup.add_basic_help(clickable,"Clickable:\nCan be hovered and pressed.") 13 | 14 | draggable = thorpy.Draggable("Draggable") 15 | thorpy.makeup.add_basic_help(draggable,"Draggable:\nYou can drag it.") 16 | 17 | checker_check = thorpy.Checker("Checker") 18 | 19 | checker_radio = thorpy.Checker("Radio", type_="radio") 20 | 21 | browser = thorpy.Browser("../../", text="Browser") 22 | 23 | browserlauncher = thorpy.BrowserLauncher(browser, const_text="Choose file:", 24 | var_text="") 25 | browserlauncher.max_chars = 15 #limit size of browser launcher 26 | 27 | dropdownlist = thorpy.DropDownListLauncher(const_text="Choose:", 28 | var_text="", 29 | titles=[str(i)*i for i in range(1, 9)]) 30 | dropdownlist.scale_to_title() 31 | dropdownlist.max_chars = 12 #limit size of drop down list 32 | 33 | slider = thorpy.SliderX(80, (5, 12), "Slider: ", type_=float, 34 | initial_value=8.4) 35 | 36 | inserter = thorpy.Inserter(name="Inserter: ", value="Write here.") 37 | 38 | title_element = thorpy.make_text("Overview example", 22, (255,0,0)) 39 | 40 | elements = [text, line, element, clickable, draggable, checker_check, 41 | checker_radio, dropdownlist, browserlauncher, slider, inserter] 42 | central_box = thorpy.Box(elements=elements) 43 | central_box.fit_children(margins=(30,30)) #we want big margins 44 | central_box.center() #center on screen 45 | central_box.add_lift() #add a lift (useless since box fits children) 46 | central_box.set_main_color((220,220,220,180)) #set box color and opacity 47 | 48 | background = thorpy.Background(image=thorpy.style.EXAMPLE_IMG, 49 | elements=[title_element, central_box]) 50 | thorpy.store(background) 51 | 52 | menu = thorpy.Menu(background) 53 | menu.play() 54 | 55 | application.quit() -------------------------------------------------------------------------------- /examples/radiobuttons.py: -------------------------------------------------------------------------------- 1 | import thorpy 2 | 3 | ap = thorpy.Application((300,300)) 4 | 5 | #format of a Pool : thorpy.Pool(elements, first_value, always_value) 6 | # can be None, and is the element that is 'on' at the beginning 7 | # =True means that there must always be an element which is 'on' 8 | 9 | #make radios 10 | radios = [thorpy.Checker("radio"+str(i), type_="radio") for i in range(4)] 11 | radio_pool = thorpy.RadioPool(radios, first_value=radios[2], always_value=True) 12 | 13 | #make togglable buttons 14 | buttons = [thorpy.Togglable("togglable"+str(i)) for i in range(4)] 15 | togglable_pool = thorpy.TogglablePool(buttons, first_value=buttons[1], 16 | always_value=False) 17 | 18 | #cosmetic separation line 19 | line = thorpy.Line(200, "h") 20 | 21 | bck = thorpy.Background.make((220,220,255), elements=radios+[line]+buttons) 22 | thorpy.store(bck) 23 | menu = thorpy.Menu(bck) 24 | menu.play() 25 | ap.quit() -------------------------------------------------------------------------------- /examples/reactions.py: -------------------------------------------------------------------------------- 1 | #ThorPy reactions tutorial : step 3 - Dynamically modify events 2 | import thorpy, pygame 3 | 4 | def my_func_reaction2(): #constant reaction do not take event as first arg 5 | info_text.set_text("Reaction 2") 6 | info_text.center() 7 | background.unblit_and_reblit() 8 | print("reaction 2 launched") 9 | 10 | def my_func_reaction1(el, reac_1): 11 | new_reaction = thorpy.ConstantReaction(reacts_to=pygame.MOUSEBUTTONDOWN, 12 | reac_func=my_func_reaction2) 13 | el.remove_reaction(reac_1) 14 | el.add_reaction(new_reaction) 15 | thorpy.functions.refresh_current_menu() #tell menu to refresh reactions! 16 | info_text.set_text("Reaction 1 will never be launched again") 17 | info_text.center() 18 | background.unblit_and_reblit() 19 | print("reaction 1 launched - replacing reac 1 by reac 2") 20 | 21 | application = thorpy.Application(size=(300, 300), caption="Reaction tuto") 22 | 23 | info_text = thorpy.make_text("No reaction launched") 24 | info_text.center() 25 | 26 | background = thorpy.Background(elements=[info_text], color=(255,255,255)) 27 | 28 | reac_1 = thorpy.ConstantReaction(reacts_to=pygame.MOUSEBUTTONDOWN, 29 | reac_func=my_func_reaction1, 30 | params={"el":background, 31 | "reac_1":None}) 32 | reac_1.params["reac_1"] = reac_1 33 | background.add_reaction(reac_1) 34 | 35 | menu = thorpy.Menu(background) 36 | menu.play() 37 | 38 | application.quit() -------------------------------------------------------------------------------- /examples/shadows.py: -------------------------------------------------------------------------------- 1 | import pygame, thorpy 2 | 3 | ap = thorpy.Application((500,400), "Shadows example") 4 | 5 | e_img = thorpy.Draggable(finish=False) 6 | image_path = "../documentation/examples/character.png" 7 | painter = thorpy.painters.imageframe.ImageFrame(image_path, 8 | colorkey=(255,255,255)) 9 | e_img.set_painter(painter) 10 | e_img.finish() #don't forget to finish 11 | 12 | if thorpy.constants.CAN_SHADOWS: 13 | thorpy.makeup.add_static_shadow(e_img, {"target_altitude":0, 14 | "shadow_radius":3}) 15 | 16 | e_text = thorpy.make_text("Drag the image", 20, (255,0,0)) 17 | e_text.stick_to("screen", "top", "top") 18 | 19 | e_background = thorpy.Background(image=thorpy.style.EXAMPLE_IMG, 20 | elements=[e_img, e_text]) 21 | 22 | menu = thorpy.Menu(e_background) 23 | menu.play() 24 | 25 | ap.quit() 26 | 27 | #examples of shadow arguments: 28 | ##thorpy.makeup.add_static_shadow(e_img, 29 | ## {"target_altitude":10, # altitude (in pixels) of the target 30 | ## "shadow_radius":3, # the smaller, the sharper is the shadow 31 | ## "sun_angle":60, # angle in degrees of the sun 32 | ## "shadow_color":(255,200,0), # color of the shadow 33 | ## "alpha_factor":0.5, # alpha factor 34 | ## "decay_mode":"linear"}) # linear or exponential -------------------------------------------------------------------------------- /examples/specialeffects.py: -------------------------------------------------------------------------------- 1 | import pygame, math, random 2 | from pygame.math import Vector2 as V2 3 | import thorpy 4 | 5 | def refresh(): 6 | pos = pygame.mouse.get_pos() 7 | e_ship.set_center(pos) #ship follows mouse 8 | # process smoke 9 | smokegen1.kill_old_elements() 10 | smokegen2.kill_old_elements() 11 | pressed = pygame.mouse.get_pressed() 12 | if pressed[0]: #left mouse button 13 | smokegen1.generate(V2(pos)) 14 | elif pressed[2]: #left mouse button 15 | smokegen2.generate(V2(pos)) 16 | smokegen1.update_physics(V2(0)) 17 | smokegen2.update_physics(V2(0)) 18 | # process debris 19 | debrisgen.kill_old_elements(screen.get_rect()) 20 | debrisgen.update_physics(dt=0.1) 21 | # refresh screen 22 | e_background.blit() 23 | debrisgen.draw(thorpy.get_screen()) 24 | smokegen1.draw(screen) 25 | smokegen2.draw(screen) 26 | pygame.display.flip() 27 | 28 | 29 | def make_debris(): 30 | angle = random.randint(0,360) #pick random angle 31 | spread = 15 #spread of debris directions 32 | debrisgen.generate( V2(pygame.mouse.get_pos()), #position 33 | n=20, #number of debris 34 | v_range=(10,50), #translational velocity range 35 | omega_range=(5,25), #rotational velocity range 36 | angle_range=(angle-spread,angle+spread)) 37 | 38 | # ############################################################################## 39 | 40 | app = thorpy.Application((400,400), "Effects") 41 | 42 | smokegen1 = thorpy.fx.get_smokegen(n=50, color=(200,200,255), grow=0.6) 43 | smokegen2 = thorpy.fx.get_fire_smokegen(n=50, color=(200,255,155), grow=0.4) 44 | debrisgen = thorpy.fx.get_debris_generator(duration=200, #nb. frames before die 45 | color=(100,100,100), 46 | max_size=10) 47 | 48 | e_ship = thorpy.Image("../documentation/examples/boat_example.png", 49 | colorkey=(255,255,255)) 50 | if thorpy.constants.CAN_SHADOWS: #set shadow 51 | thorpy.makeup.add_static_shadow(e_ship, {"target_altitude":5, 52 | "shadow_radius":3, 53 | "sun_angle":40, 54 | "alpha_factor":0.6}) 55 | 56 | e_background = thorpy.Background(image=thorpy.style.EXAMPLE_IMG, 57 | elements=[e_ship]) 58 | 59 | reac_time = thorpy.ConstantReaction(thorpy.constants.THORPY_EVENT, refresh, 60 | {"id":thorpy.constants.EVENT_TIME}) 61 | reac_space = thorpy.ConstantReaction(pygame.KEYDOWN, make_debris, 62 | {"key":pygame.K_SPACE}) 63 | e_background.add_reactions([reac_time, reac_space]) 64 | 65 | screen = thorpy.get_screen() 66 | infotext = "Press SPACE to spawn debris\n"+\ 67 | "LMB and RMB to spawn smokes\n"+\ 68 | "and move the mouse to move the boat." 69 | thorpy.launch_blocking_alert("Commands", infotext) 70 | menu = thorpy.Menu(e_background) 71 | pygame.key.set_repeat(30,30) 72 | menu.play() 73 | app.quit() -------------------------------------------------------------------------------- /examples/storage.py: -------------------------------------------------------------------------------- 1 | #ThorPy storage tutorial : manual placing 2 | import thorpy, random 3 | 4 | application = thorpy.Application(size=(400, 400), caption="Storage") 5 | 6 | elements = [thorpy.make_button("button" + str(i)) for i in range(13)] 7 | for e in elements: 8 | w, h = e.get_rect().size 9 | w, h = w*(1+random.random()/2.), h*(1+random.random()/2.) 10 | e.set_size((w,h)) 11 | elements[6] = thorpy.Element(text="") 12 | elements[6].set_size((100,100)) 13 | 14 | elements[0].set_topleft((10, 300)) 15 | elements[1].set_topleft(elements[0].get_rect().bottomright) 16 | elements[2].set_center((100, 200)) 17 | elements[3].stick_to(elements[2], target_side="bottom", self_side="top") 18 | elements[4].stick_to(elements[2], target_side="right", self_side="left") 19 | 20 | background = thorpy.Background(color=(200, 200, 255), elements=elements) 21 | thorpy.store(background, elements[6:12], x=380, align="right") 22 | elements[5].center(element=elements[6]) 23 | elements[5].rank = elements[6].rank + 0.1 #be sure number 5 is blitted after 6 24 | background.sort_children_by_rank() #tell background to sort its children 25 | elements[12].set_location((0.1, 0.2)) #relative placing of number 12 26 | 27 | menu = thorpy.Menu(background) 28 | menu.play() 29 | 30 | application.quit() -------------------------------------------------------------------------------- /examples/submenus.py: -------------------------------------------------------------------------------- 1 | import thorpy 2 | 3 | #Declaration of the application in which the menu is going to live. 4 | application = thorpy.Application(size=(500, 500)) 5 | 6 | #Setting the graphical theme. By default, it is 'classic' (windows98-like). 7 | ##thorpy.theme.set_theme('human') 8 | 9 | #Declaration of some elements... 10 | useless1 = thorpy.Element("This button is useless.\nAnd you can't click it.") 11 | useless1.set_pressed_state() #so user knows he can't click 12 | useless1.scale_to_content() 13 | 14 | text = "This button also is useless.\nBut you can click it anyway." 15 | useless2 = thorpy.make_button(text) 16 | 17 | draggable = thorpy.Draggable("Drag me!") 18 | draggable.scale_to_content() 19 | 20 | box1 = thorpy.make_ok_box([useless1, useless2, draggable]) 21 | options1 = thorpy.make_button("Some useless things...") 22 | thorpy.set_launcher(options1, box1) 23 | 24 | 25 | inserter = thorpy.Inserter(name="Tip text: ", 26 | value="This is a default text.", 27 | size=(150, 20)) 28 | 29 | file_browser = thorpy.Browser(path="C:/Users/", text="Please have a look.") 30 | 31 | browser_launcher = thorpy.BrowserLauncher(browser=file_browser, 32 | const_text="Choose a file: ", 33 | var_text="") 34 | browser_launcher.scale_to_title() 35 | 36 | color_setter = thorpy.ColorSetter.make() 37 | color_launcher = thorpy.ColorSetterLauncher(color_setter, 38 | "Launch color setter") 39 | 40 | options2 = thorpy.make_button("Useful things") 41 | box2 = thorpy.make_ok_box([inserter, color_launcher, browser_launcher]) 42 | thorpy.set_launcher(options2, box2) 43 | 44 | quit_button = thorpy.make_button("Quit") 45 | quit_button.set_as_exiter() 46 | 47 | central_box = thorpy.Box.make([options1, options2, quit_button]) 48 | central_box.set_main_color((200, 200, 200, 120)) 49 | central_box.center() 50 | 51 | #Declaration of a background element - include your own path! 52 | background = thorpy.Background(image=thorpy.style.EXAMPLE_IMG, 53 | elements=[central_box]) 54 | 55 | menu = thorpy.Menu(elements=background, fps=45) 56 | menu.play() 57 | 58 | application.quit() -------------------------------------------------------------------------------- /examples/userchoices.py: -------------------------------------------------------------------------------- 1 | import thorpy 2 | """ 3 | In this example, a box opens in which the user can choose between turning the 4 | background blue or red (or do nothing). 5 | """ 6 | 7 | def set_blue(): 8 | background.set_main_color((0,0,255)) 9 | background.unblit_and_reblit() 10 | 11 | def set_red(): 12 | background.set_main_color((255,0,0)) 13 | background.unblit_and_reblit() 14 | 15 | def my_choices_1(): 16 | choices = [("I like blue",set_blue), ("No! red",set_red), ("cancel",None)] 17 | thorpy.launch_nonblocking_choices("This is a non-blocking choices box!\n", 18 | choices) 19 | print("Proof that it is non-blocking : this sentence is printing!") 20 | 21 | def my_choices_2(): 22 | choices = [("I like blue",set_blue), ("No! red",set_red), ("cancel",None)] 23 | thorpy.launch_blocking_choices("Blocking choices box!\n", choices, 24 | parent=background) #for auto unblit 25 | print("This sentence will print only after you clicked ok") 26 | 27 | application = thorpy.Application((500,500), "Launching alerts") 28 | 29 | button1 = thorpy.make_button("Non-blocking version", func=my_choices_1) 30 | button2 = thorpy.make_button("Blocking version", func=my_choices_2) 31 | 32 | background = thorpy.Background(elements=[button1,button2]) 33 | thorpy.store(background) 34 | 35 | menu = thorpy.Menu(background) 36 | menu.play() 37 | 38 | application.quit() -------------------------------------------------------------------------------- /examples/userchoices2.py: -------------------------------------------------------------------------------- 1 | import thorpy 2 | 3 | application = thorpy.Application(size=(500, 500)) 4 | 5 | thorpy.set_theme("round") 6 | 7 | def launch_menu(choices): 8 | title = thorpy.make_text("Choose something", 14, (255,0,0)) 9 | #now define the behaviour of clicked elements 10 | def at_press(what): 11 | some_text.set_text(what) #change the element content (see below) 12 | thorpy.functions.quit_menu_func() #exit the menu 13 | thorpy.store(background) #align the elements (size has changed) 14 | background.unblit_and_reblit() 15 | #dynamically create elements 16 | elements = [] 17 | for text in choices: 18 | element = thorpy.make_button(text, func=at_press) 19 | element.user_params = {"what":text} 20 | elements.append(element) 21 | #box to store everything 22 | box = thorpy.Box([title] + elements) 23 | box.set_main_color((200,200,200,150)) 24 | box.center() 25 | m = thorpy.Menu(box) 26 | m.play() 27 | 28 | some_text = thorpy.make_text("Click on the button below to change the text.", 18) 29 | choices = ["My new text", "Another proposition", "Blah", "blah"] 30 | button = thorpy.make_button("Change text", launch_menu, {"choices":choices}) 31 | background = thorpy.Background(elements=[some_text,button]) 32 | thorpy.store(background) 33 | 34 | 35 | menu = thorpy.Menu(background) 36 | menu.play() 37 | 38 | application.quit() -------------------------------------------------------------------------------- /examples/usewithpygame.py: -------------------------------------------------------------------------------- 1 | #Tutorial : how to use ThorPy with a pre-existing code 2 | import pygame, thorpy 3 | import thorpy.miscgui.functions 4 | pygame.init() 5 | pygame.key.set_repeat(300, 30) 6 | screen = pygame.display.set_mode((400,400)) 7 | screen.fill((255,255,255)) 8 | rect = pygame.Rect((0, 0, 50, 50)) 9 | rect.center = screen.get_rect().center 10 | rect.y += 100 11 | clock = pygame.time.Clock() 12 | 13 | pygame.draw.rect(screen, (255,0,0), rect) 14 | pygame.display.flip() 15 | 16 | #declaration of some ThorPy elements ... 17 | slider = thorpy.SliderX(100, (12, 35), "My Slider") 18 | button = thorpy.make_button("Quit", func=thorpy.functions.quit_func) 19 | text = thorpy.make_text("Left arrow to move the red square") 20 | box = thorpy.Box(elements=[slider,button, text]) 21 | #we regroup all elements on a menu, even if we do not launch the menu 22 | menu = thorpy.Menu(box) 23 | thorpy.miscgui.functions.set_current_menu(menu) 24 | #important : set the screen as surface for all elements 25 | for element in menu.get_population(): 26 | element.surface = screen 27 | #use the elements normally... 28 | box.set_topleft((100,100)) 29 | box.blit() 30 | box.update() 31 | 32 | playing_game = True 33 | while playing_game: 34 | clock.tick(45) 35 | for event in pygame.event.get(): 36 | if event.type == pygame.QUIT: 37 | playing_game = False 38 | break 39 | elif event.type == pygame.KEYDOWN: 40 | if event.key == pygame.K_LEFT: 41 | pygame.draw.rect(screen, (255,255,255), rect) #delete old 42 | pygame.display.update(rect) 43 | rect.move_ip((-5,0)) 44 | pygame.draw.rect(screen, (255,0,0), rect) #drat new 45 | pygame.display.update(rect) 46 | menu.react(event) #the menu automatically integrate your elements 47 | 48 | pygame.quit() -------------------------------------------------------------------------------- /gamestools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/gamestools/__init__.py -------------------------------------------------------------------------------- /gamestools/commands.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import thorpy 3 | 4 | def writing(): 5 | thorpy.parameters.KEY_DELAY = 30 6 | thorpy.parameters.KEY_INTERVAL = 100 7 | pygame.key.set_repeat(30,500) 8 | 9 | def playing(fps): 10 | value = 1000//fps 11 | thorpy.parameters.KEY_DELAY = value 12 | thorpy.parameters.KEY_INTERVAL = value 13 | pygame.key.set_repeat(value,value) 14 | 15 | class Commands: 16 | 17 | def __init__(self, element, delta_i=-1): 18 | self.e = element 19 | self._initialize() 20 | self.last_key_action = -float("inf") 21 | self.i = 0 22 | self.delta_i = delta_i 23 | self.cam_shift = [0,0] 24 | self.reac = {} 25 | self.refresh = None 26 | 27 | def _initialize(self): 28 | self.reac_keydown = thorpy.Reaction(pygame.KEYDOWN, self.func_keydown) 29 | self.e.add_reaction(self.reac_keydown) 30 | # 31 | self.reac_time = thorpy.ConstantReaction(thorpy.constants.THORPY_EVENT, 32 | self.func_time, 33 | {"id":thorpy.constants.EVENT_TIME}) 34 | self.e.add_reaction(self.reac_time) 35 | 36 | def func_keydown(self, e): 37 | if self.i > self.last_key_action + self.delta_i: 38 | self.last_key_action = self.i 39 | reaction = self.reac.get(e.key) 40 | if reaction: 41 | reaction() 42 | 43 | def func_time(self): 44 | if self.refresh: 45 | self.refresh() 46 | self.i += 1 47 | 48 | def add_reaction(self, key, func): 49 | if key in self.reac: 50 | raise Exception("There is already a func for this key") 51 | self.reac[key] = func 52 | 53 | def default_func(self): 54 | pass -------------------------------------------------------------------------------- /gamestools/grid.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | from thorpy.gamestools.basegrid import BaseGrid 4 | 5 | NA = [(1, 0), (-1, 0), (0, 1), (0, -1)] 6 | NB = [(1, 1), (1, -1), (-1, 1), (-1, -1)] + NA 7 | 8 | 9 | class Grid(BaseGrid): 10 | 11 | def __init__(self, nx, ny, cell_size, topleft=(0,0), value=None, 12 | periodicity=(False, False)): 13 | BaseGrid.__init__(self, nx, ny, value, periodicity) 14 | self.cell_w = cell_size[0] 15 | self.cell_h = cell_size[1] 16 | self.frame = pygame.Rect(topleft, 17 | (self.nx*self.cell_w, self.ny*self.cell_h)) 18 | self.cell_rect = self.build_cell_rect() 19 | 20 | def __len__(self): 21 | return self.nx*self.ny 22 | 23 | def copy(self): 24 | copied = BaseGrid.copy(self) 25 | copied.cell_w = self.cell_w 26 | copied.cell_h = self.cell_h 27 | copied.frame = self.frame.copy() 28 | copied.cell_rect = self.cell_rect.copy() 29 | return copied 30 | 31 | def build_cell_rect(self): 32 | topleft = (self.frame.left, self.frame.bottom - self.cell_h) 33 | return pygame.Rect(topleft, (self.cell_w, self.cell_h)) 34 | 35 | def move(self, shift): 36 | self.frame.move_ip(shift) 37 | self.cell_rect = self.build_cell_rect() 38 | 39 | def set_topleft(self, pos): 40 | self.frame.topleft = pos 41 | self.cell_rect = self.build_cell_rect() 42 | 43 | def set_bottomleft(self, pos): 44 | self.frame.bottomleft = pos 45 | self.cell_rect = self.build_cell_rect() 46 | 47 | def set_center(self, pos): 48 | self.frame.center = pos 49 | self.cell_rect = self.build_cell_rect() 50 | 51 | def center_on(self, pos): 52 | if isinstance(pos, pygame.Rect): 53 | pos = pos.center 54 | self.set_center(pos) 55 | 56 | def iterrects(self): 57 | for coord in self: 58 | yield self.get_rect_at_coord(coord) 59 | 60 | def get_rect_at_coord(self, coord): 61 | shift_x = coord[0] * self.cell_w 62 | shift_y = coord[1] * self.cell_h 63 | return self.cell_rect.move((shift_x, -shift_y)) 64 | 65 | def get_coord_at_pix(self, pix): 66 | x = pix[0] - self.frame.left 67 | y = self.frame.bottom - pix[1] 68 | cx = int(x * self.nx / self.frame.width) 69 | cy = int(y * self.ny / self.frame.height) 70 | return (cx, cy) 71 | 72 | def get_rect_at_pix(self, pix): 73 | return self.get_rect_at_coord(self.get_coord_at_pix(pix)) 74 | 75 | ## def blit_cell_on(self, surface, color, coord, thick=1): 76 | ## r = self.get_rect_at_coord(coord) 77 | ## pygame.draw.rect(surface, color, r, thick) 78 | ## 79 | ## def blit_on(self, surface, cell_color=(0, 0, 0), frame_color=(0,0,0)): 80 | ## for coord in self: 81 | ## self.blit_cell_on(surface, color, coord) 82 | ## pygame.draw.rect(surface, frame_color, self.frame, 1) 83 | 84 | class PygameGrid(Grid): 85 | 86 | def build_cell_rect(self): 87 | return pygame.Rect(self.frame.topleft, (self.cell_w, self.cell_h)) 88 | 89 | def get_rect_at_coord(self, coord): 90 | shift_x = coord[0] * self.cell_w 91 | shift_y = coord[1] * self.cell_h 92 | return self.cell_rect.move((shift_x, shift_y)) 93 | 94 | def get_coord_at_pix(self, pix): 95 | x = pix[0] - self.frame.left 96 | y = pix[1] - self.frame.bottom 97 | cx = int(x * self.nx / self.frame.width) 98 | cy = int(y * self.ny / self.frame.height) 99 | return (cx, cy) 100 | 101 | def get_rect_at_pix(self, pix): 102 | return self.get_rect_at_coord(self.get_coord_at_pix(pix)) -------------------------------------------------------------------------------- /gamestools/hud.py: -------------------------------------------------------------------------------- 1 | import math, pygame 2 | from thorpy import load_image 3 | from thorpy.miscgui import style 4 | 5 | 6 | class HeartLife: 7 | 8 | def __init__(self, full=style.HEART_FULL, empty=style.HEART_EMPTY, n=5, 9 | spacing=5, x0=5, y0=5, size=None): 10 | self.full = load_image(full,(255,255,255)) 11 | self.empty = load_image(empty,(255,255,255)) 12 | if size: 13 | self.full = pygame.transform.scale(self.full, size) 14 | self.empty = pygame.transform.scale(self.empty, size) 15 | self.n = n 16 | self.heart_size = self.full.get_size() 17 | self.heart_spacing = self.heart_size[0] + spacing 18 | self.xlife = - self.heart_spacing + x0 19 | self.ylife = y0 20 | 21 | def blit(self, surf, life): 22 | x = 0 23 | nfull = math.ceil(life*self.n) 24 | for i in range(nfull): 25 | x += self.heart_spacing 26 | surf.blit(self.full,(self.xlife+x,self.ylife)) 27 | for i in range(self.n-nfull): 28 | x += self.heart_spacing 29 | surf.blit(self.empty,(self.xlife+x,self.ylife)) 30 | 31 | def move(self, delta): 32 | self.xlife += delta[0] 33 | self.ylife += delta[1] 34 | 35 | def set_topleft(self, topleft): 36 | self.xlife, self.ylife = topleft 37 | -------------------------------------------------------------------------------- /gamestools/monitoring.py: -------------------------------------------------------------------------------- 1 | import time 2 | class Monitor: 3 | 4 | def append(self, name): 5 | if not hasattr(self, name): 6 | setattr(self, name, [time.perf_counter()]) 7 | else: 8 | getattr(self,name).append(time.perf_counter()) 9 | 10 | def show(self, letters=None, rnd=None): 11 | if not letters: 12 | letters = [] 13 | for letter in "abcdefghijklmnopqrstuvwxyz": 14 | if hasattr(self,letter): 15 | letters.append(letter) 16 | n_lett = len(letters) 17 | tot = [0.]*n_lett 18 | L = len(getattr(self,letters[0])) 19 | print("Stats on", L, "iterations:") 20 | for i in range(1,n_lett): 21 | for k in range(L): #k is the iteration 22 | diff = getattr(self,letters[i])[k] - getattr(self,letters[i-1])[k] 23 | tot[i] += diff 24 | for k in range(1,L): #z->a_previous_iteration 25 | diff = getattr(self,letters[0])[k] - getattr(self,letters[n_lett-1])[k-1] 26 | tot[0] += diff 27 | for i in list(range(1,len(tot)))+[0]: #we want z->a displayed at the end 28 | if rnd is None: 29 | n = tot[i] 30 | else: 31 | n = round(tot[i], rnd) 32 | p = round(100*n/sum(tot)) 33 | print(letters[i-1]+"->"+letters[i]+": "+str(n)+" ("+str(p)+"%)") -------------------------------------------------------------------------------- /gamestools/sound.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | #Usage example: 3 | ##c = SoundCollection() 4 | ##c.add("ok.wav", "ok") 5 | ##c.ok.play() #or wathever the pygame Sound class allows 6 | 7 | class FakeSound(object): 8 | 9 | def play(self, loop=0): 10 | pass 11 | 12 | def play_next_channel(self): 13 | pass 14 | 15 | def stop(self): 16 | pass 17 | 18 | class Sound(pygame.mixer.Sound): 19 | 20 | def __init__(self, filename, manager): 21 | filename = filename.replace("\\","/") 22 | pygame.mixer.Sound.__init__(self, filename) 23 | self.manager = manager 24 | 25 | def play_next_channel(self): 26 | self.play() 27 | ## self.manager.current_channel_number += 1 28 | ## self.manager.current_channel_number %= pygame.mixer.get_num_channels() 29 | ## c = self.manager.current_channel_number 30 | ## if not c in self.manager.reserved_channels: 31 | ## self.manager.current_channel = pygame.mixer.Channel(c) 32 | ## self.manager.current_channel.play(self) 33 | 34 | class SoundCollection: 35 | 36 | def __init__(self): 37 | pygame.mixer.pre_init(44100, 16, 2, 4096) #frequency, size, channels, buffersize 38 | ## pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512) 39 | pygame.mixer.init(frequency=22050, size=-16, buffer=512) 40 | self.current_channel_number = 0 41 | self.current_channel = pygame.mixer.Channel(self.current_channel_number) 42 | self.reserved_channels = set() 43 | 44 | def reserve_current_channel(self): 45 | self.reserved_channels.add(self.current_channel_number) 46 | 47 | def add(self, filename, name=None): 48 | pygame.mixer.init() 49 | fake = False 50 | try: 51 | sound = Sound(filename, manager=self) 52 | sound.play() 53 | pygame.mixer.stop() 54 | print("Loaded", filename, name) 55 | except FileNotFoundError: 56 | sound = FakeSound() 57 | fake = True 58 | print("Couldn't load", filename, name) 59 | if name: 60 | setattr(self, name, sound) 61 | return sound, fake 62 | 63 | 64 | def play_music(name, n=0): 65 | try: 66 | pygame.mixer.music.fadeout(1000) 67 | pygame.mixer.music.load(name) 68 | pygame.mixer.music.play(n) 69 | except: 70 | pass 71 | -------------------------------------------------------------------------------- /gamestools/writing.py: -------------------------------------------------------------------------------- 1 | class LargeTextManager: 2 | 3 | def __init__(self, indent=" ", endpar="\n\n"): 4 | self.indent = indent 5 | self.endpar = endpar 6 | self.paragraphs = [] 7 | 8 | def paragraph(self,*text): 9 | """Add a new paragraph""" 10 | if len(self.paragraphs) > 0: 11 | self.end_paragraph() 12 | content = self.indent 13 | else: 14 | content ="" 15 | for t in text: 16 | content += t 17 | self.paragraphs.append(content) 18 | 19 | def end_paragraph(self): 20 | self.paragraphs[-1] += self.endpar 21 | 22 | def more(self, *text): 23 | """Append to the current paragraph""" 24 | for t in text: 25 | self.paragraphs[-1] += t 26 | 27 | def get_all(self): 28 | text = "" 29 | for p in self.paragraphs: 30 | text += p 31 | return text -------------------------------------------------------------------------------- /menus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/menus/__init__.py -------------------------------------------------------------------------------- /menus/tickedmenu.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | from thorpy.menus.basicmenu import BasicMenu 4 | from thorpy.miscgui import constants 5 | from thorpy.elements.ghost import Ghost 6 | 7 | 8 | class TickedMenu(BasicMenu): 9 | """Post time since last frame""" 10 | 11 | def post_time_event(self): 12 | tick_ = self.clock.get_time() 13 | event = pygame.event.Event(constants.THORPY_EVENT, 14 | id=constants.EVENT_TIME, 15 | tick=tick_) 16 | pygame.event.post(event) 17 | 18 | def react_to_all_events(self): 19 | ## self.clock_tick(self.fps) 20 | self.post_time_event() 21 | for event in pygame.event.get(): 22 | self.treatement(event) 23 | 24 | def interactive_pause(max_time_in_seconds, element=None, fps=45): 25 | if element is None: 26 | element = Ghost() 27 | menu = TickedMenu(element, fps=fps) 28 | menu.kill_after(menu.fps * max_time_in_seconds) 29 | menu.play() 30 | return menu.kill_after 31 | -------------------------------------------------------------------------------- /miscgui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/miscgui/__init__.py -------------------------------------------------------------------------------- /miscgui/_ghoststate.py: -------------------------------------------------------------------------------- 1 | """State with no graphical existence""" 2 | from pygame.rect import Rect 3 | 4 | 5 | def get_same_state(state): 6 | tl = state.ghost_rect.topleft 7 | size = state.ghost_rect.size 8 | return _GhostState(tl, size) 9 | 10 | 11 | class _GhostState(object): 12 | 13 | def __init__(self, topleft=(0, 0), size=(0, 0)): 14 | self.ghost_rect = Rect(topleft, size) 15 | 16 | def set_ghost_rect(self, topleft, size): 17 | self.ghost_rect.topleft = topleft 18 | self.ghost_rect.size = size 19 | 20 | def set_topleft(self, topleft): 21 | self.ghost_rect.topleft = topleft 22 | 23 | def move(self, shift): 24 | """Move and refresh using set_topleft""" 25 | pos_x = shift[0] + self.ghost_rect.x 26 | pos_y = shift[1] + self.ghost_rect.y 27 | self.set_topleft((pos_x, pos_y)) 28 | -------------------------------------------------------------------------------- /miscgui/_screened.py: -------------------------------------------------------------------------------- 1 | """Module for creating _Screened objects, that are designed to be blitted on 2 | the screen. 3 | """ 4 | from pygame import display 5 | 6 | 7 | class _Screened(object): 8 | """Object that is designed to be blitted on the screen. Implements 9 | methods for setting the screen clips. 10 | """ 11 | 12 | def __init__(self): 13 | """Object that is designed to be blitted on the screen.""" 14 | ## self.surface = display.get_surface() 15 | self._old_clips = [None] # fifo 16 | 17 | def _add_old_clip(self): 18 | """Add the current clip of the screen to the list of previous clips. 19 | """ 20 | self._old_clips.insert(0, self.surface.get_clip()) 21 | 22 | def _unclip_screen(self): 23 | """Restore the oldest of the previous clips as the clip of the screen, 24 | and removes it from the previous clips list. 25 | """ 26 | self.surface.set_clip(self._old_clips.pop(0)) 27 | -------------------------------------------------------------------------------- /miscgui/keyer.py: -------------------------------------------------------------------------------- 1 | """Module defining default keyboard behaviour.""" 2 | 3 | import pygame 4 | from pygame.locals import * 5 | 6 | from thorpy.miscgui.functions import debug_msg 7 | 8 | 9 | 10 | MODIFIERS = [K_RSHIFT, 11 | K_LSHIFT, 12 | K_RCTRL, 13 | K_LCTRL, 14 | K_RALT, 15 | K_LALT, 16 | K_RMETA, 17 | K_LMETA] 18 | 19 | QWERTZ_SPECIALS = {(K_RALT, K_3): K_HASH, # "#" 20 | (K_RALT, K_2): K_AT, # "@" 21 | (K_LSHIFT, K_MINUS): K_QUESTION, # "?" 22 | (K_LSHIFT, K_PERIOD): K_COLON, # ":" 23 | (K_LSHIFT, K_COMMA): K_SEMICOLON, # ";" 24 | (K_LSHIFT, K_7): K_SLASH, # "/" 25 | (K_LSHIFT, K_LESS): K_GREATER, # ">" 26 | (K_LSHIFT, K_3): K_ASTERISK, # "*" 27 | } 28 | SPECIALS = {} 29 | 30 | 31 | QWERTZ_CHANGES = {K_z: K_y, 32 | K_y: K_z, 33 | K_SLASH: K_MINUS} # example 34 | CHANGES = {} 35 | 36 | 37 | def set_qwertz(): 38 | global SPECIALS, CHANGES 39 | SPECIALS = QWERTZ_SPECIALS.copy() 40 | CHANGES = QWERTZ_CHANGES.copy() 41 | 42 | def set_default(): 43 | global SPECIALS, CHANGES 44 | SPECIALS = {} 45 | CHANGES = {} 46 | 47 | class Keyer(object): 48 | # Penser a permettre les set blocked et allowed 49 | 50 | def __init__(self, specials=None, modifiers=None, changes=None): 51 | if not specials: 52 | specials = SPECIALS 53 | if not modifiers: 54 | modifiers = MODIFIERS 55 | if not changes: 56 | changes = CHANGES 57 | self.specials = specials 58 | self.changes = changes 59 | self.modifiers = modifiers 60 | 61 | def _get_changed(self, key): 62 | """Performs the 'translation' between and its corresponding value 63 | according to self.changes's dict. 64 | """ 65 | changed = self.changes.get(key) 66 | if changed: 67 | return changed 68 | else: 69 | return key 70 | 71 | def _more_than_256(self, key): 72 | key = pygame.key.name(key) 73 | if key.startswith("["): 74 | if key.endswith("]"): 75 | return key[1:-1] 76 | return key 77 | 78 | def get_char_from_key(self, key): 79 | """ is returned if no character can be found from """ 80 | pressed = pygame.key.get_pressed() 81 | pygame.event.pump() 82 | for (ka, kb) in self.specials: # handle combinations 83 | if pressed[ka]: 84 | if kb == key: 85 | key = self.specials[(ka, kb)] 86 | if key < 256: 87 | return chr(key) 88 | key = self._get_changed(key) 89 | if pressed[K_LSHIFT]: # handle caps 90 | if key >= 32 and key <= 126: 91 | key -= 32 92 | debug_msg("key interpretation :", key, pygame.key.name(key)) 93 | if key < 256: 94 | return chr(key) 95 | else: 96 | return self._more_than_256(key) 97 | -------------------------------------------------------------------------------- /miscgui/launchers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/miscgui/launchers/__init__.py -------------------------------------------------------------------------------- /miscgui/metadata.py: -------------------------------------------------------------------------------- 1 | import json, pygame 2 | 3 | import thorpy.miscgui.theme as theme 4 | 5 | 6 | class MetaDataManager(object): 7 | 8 | def __init__(self, data=None): 9 | if data is None: data = {} 10 | self.data = data 11 | 12 | def write_data(self, fn): 13 | json.dump(self.data, open(fn,'w')) 14 | 15 | def read_data(self, fn): 16 | try: 17 | self.data = json.load(open(fn)) 18 | except: 19 | return None 20 | return True 21 | 22 | def load_font_data(self, fn): 23 | font = self.data.get("font") 24 | if font: 25 | theme.add_font(font) 26 | font_size = self.data.get("font_size") 27 | if font_size: 28 | theme.set_font_sizes(font_size) 29 | 30 | def get_display_data(self, fn, w, h, flags): 31 | W,H = self.data.get("screen_w"), self.data.get("screen_h") 32 | fullscreen = self.data.get("fullscreen") 33 | flags = flags 34 | if fullscreen: 35 | flags |= pygame.FULLSCREEN 36 | if W is None: W = w 37 | if H is None: H = h 38 | return W,H,flags 39 | -------------------------------------------------------------------------------- /miscgui/painterstyle.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module define the default painters used to build graphical elements. 3 | """ 4 | from thorpy.painting.writer import Writer 5 | from thorpy.painting.painters.basicframe import BasicFrame 6 | from thorpy.painting.painters.classicframe import ClassicFrame 7 | from thorpy.painting.painters.classicround import ClassicRound 8 | from thorpy.painting.painters.roundrect import RoundRect 9 | from thorpy.painting.painters.optionnal.human import Human, HumanLite 10 | from thorpy.painting.painters.optionnal.rectframe import RectFrame 11 | from thorpy.painting.painters.optionnal.rectframe import Windows10Frame 12 | 13 | # default painters 14 | BASIC_PAINTER = BasicFrame 15 | DEF_PAINTER = ClassicFrame 16 | INSERTER_PAINTER = ClassicFrame 17 | INSERTER_NAME_PAINTER = BasicFrame 18 | CHECKER_NAME_PAINTER = BasicFrame 19 | CHECKER_VALUE_PAINTER = BasicFrame 20 | BROWSER_PAINTER = ClassicFrame 21 | BAR_PAINTER = ClassicFrame 22 | CHECKBOX_PAINTER = ClassicFrame 23 | RADIO_PAINTER = ClassicRound 24 | NAME_PAINTER = BasicFrame 25 | BROWSER_LAUNCHER_PAINTER = ClassicFrame 26 | HELP_PAINTER = RoundRect 27 | CURSOR_PAINTER = BasicFrame 28 | BOX_PAINTER = ClassicFrame 29 | 30 | 31 | painters = {"basic" : BasicFrame, 32 | "round" : HumanLite, 33 | "human" : Human, 34 | "classic" : ClassicFrame, 35 | "simple" : RectFrame, 36 | "windows10" : Windows10Frame} 37 | 38 | # default writers 39 | 40 | WRITER = Writer 41 | -------------------------------------------------------------------------------- /miscgui/parameters.py: -------------------------------------------------------------------------------- 1 | """This module defines miscellaneous parameters for ThorPy. These parameters may 2 | change during execution, so don't forget to import this module and then use its 3 | attributes, instead of importing them directly or to use them as default args. 4 | """ 5 | 6 | from pygame import MOUSEBUTTONDOWN, MOUSEBUTTONUP, KEYDOWN, KEYUP, K_UP, K_DOWN 7 | 8 | CHILDREN_SHIFT = (0, 10) 9 | HELP_WAIT_TIME = 500 10 | WHEEL_LIFT_SHIFT = 10 11 | WHEEL_SLIDER_SHIFT = 5 12 | CLICK_LIFT_SHIFT = 1 13 | CLICK_LIFT_REPEAT = 4 14 | LIMVALS = (0., 100.) 15 | DOUBLE_CLICK_DELAY = 400 #unit: ms 16 | 17 | LEFT_CLICK_BUTTON = 1 18 | RIGHT_CLICK_BUTTON = 3 19 | MIDDLE_CLICK_BUTTON = 2 # may be WHEEL_CLICK_BUTTON 20 | WHEELUP_BUTTON = 4 21 | WHEELDOWN_BUTTON = 5 22 | 23 | BUTTON_PRESS_EVENT = MOUSEBUTTONDOWN 24 | BUTTON_UNPRESS_EVENT = MOUSEBUTTONUP 25 | 26 | KEY_PRESS_EVENT = KEYDOWN 27 | KEY_UNPRESS_EVENT = KEYUP 28 | 29 | SLIDER_UP = K_UP 30 | SLIDER_DOWN = K_DOWN 31 | 32 | KEY_DELAY = 500 33 | KEY_INTERVAL = 30 34 | 35 | CURSOR_INTERVAL = 500 36 | 37 | FILL_SCREEN_AT_SUBMENUS = False 38 | SCREEN_FILL = (255,255,255) -------------------------------------------------------------------------------- /miscgui/reaction.py: -------------------------------------------------------------------------------- 1 | from thorpy.miscgui.constants import THORPY_EVENT, EVENT_TIME 2 | import pygame 3 | 4 | class Reaction(object): 5 | """Reacts to a pygame event by running . Designed 6 | to belong to a specific element, normally through element.reactions. 7 | """ 8 | 9 | def __init__(self, reacts_to, reac_func, event_args=None, params=None, 10 | reac_name=None): 11 | """ 12 | : pygame event 13 | : function ro run 14 | : args to filter the pygame event 15 | : constant params for the function to run (not depending on 16 | the event. 17 | : the name of the reaction (optionnal). Reaction names that 18 | are int comprised in range(13) are reserved. Use REAC_USER + n for 19 | your nth reaction, or use any other structure than int. 20 | """ 21 | if not event_args: 22 | event_args = {} 23 | if not params: 24 | params = {} 25 | self.reacts_to = reacts_to 26 | self.event_args = event_args 27 | self.reac_func = reac_func 28 | self.params = params 29 | self.reac_name = reac_name 30 | 31 | def _is_right_event(self, event): 32 | """Returns True if match the requirements for self.""" 33 | if event.type == self.reacts_to: 34 | for k in self.event_args.keys(): 35 | if event.dict.get(k) != self.event_args[k]: # if has not arg 36 | return False 37 | return True 38 | else: 39 | return False 40 | 41 | def _try_activation(self, event): 42 | """Tests if has to react to 43 | NB : the reac_func function shall always take event as argument""" 44 | if self._is_right_event(event): 45 | self.reac_func(event, **self.params) 46 | 47 | ## def __repr__(self): 48 | ## return " ".join([self.reac_name,self.reac_func.__repr__,str(self.reacts_to),str(self.event_args)]) 49 | 50 | class ConstantReaction(Reaction): 51 | """Reaction whose behaviour is not influed by event. See Reaction for more. 52 | """ 53 | 54 | def _try_activation(self, event): 55 | """Tests if has to react to """ 56 | if self._is_right_event(event): 57 | self.reac_func(**self.params) 58 | 59 | 60 | def add_time_reaction(element, func, event_args=None, params=None, reac_name=None): 61 | reac = ConstantReaction(THORPY_EVENT, func, {"id":EVENT_TIME}, params, 62 | reac_name) 63 | element.add_reaction(reac) 64 | 65 | def add_keydown_reaction(element, key, func, event_args=None, params=None, 66 | reac_name=None): 67 | reac = ConstantReaction(pygame.KEYDOWN, func, {"key":key}, params, 68 | reac_name) 69 | element.add_reaction(reac) 70 | 71 | def add_keyup_reaction(element, key, func, event_args=None, params=None, 72 | reac_name=None): 73 | reac = ConstantReaction(pygame.KEYUP, func, {"key":key}, params, 74 | reac_name) 75 | element.add_reaction(reac) 76 | 77 | def add_mousemotion_reaction(element, func, event_args=None, params=None, 78 | reac_name=None): 79 | reac = ConstantReaction(pygame.MOUSEMOTION, func, event_args, params, 80 | reac_name) 81 | element.add_reaction(reac) 82 | 83 | def add_click_reaction(element, func, event_args=None, params=None, 84 | reac_name=None): 85 | reac = ConstantReaction(pygame.MOUSEBUTTONDOWN, func, event_args, params, 86 | reac_name) 87 | element.add_reaction(reac) 88 | 89 | add_mousebuttondown_reaction = add_click_reaction 90 | 91 | def add_unclick_reaction(element, func, event_args=None, params=None, 92 | reac_name=None): 93 | reac = ConstantReaction(pygame.MOUSEBUTTONUP, func, event_args, params, 94 | reac_name) 95 | element.add_reaction(reac) 96 | 97 | add_mousebuttonup_reaction = add_unclick_reaction 98 | 99 | 100 | -------------------------------------------------------------------------------- /miscgui/state.py: -------------------------------------------------------------------------------- 1 | """Couple of fusionner, position and ghost rect""" 2 | 3 | from thorpy.miscgui._ghoststate import _GhostState 4 | from thorpy._utils.rectscomputing import get_rel_pos_topleft 5 | 6 | from thorpy.miscgui.title import Title 7 | from thorpy.painting.painters.painter import Painter 8 | from thorpy.painting.fusionner import _Fusionner 9 | 10 | def get_void_state(): 11 | title = Title("") 12 | painter = Painter(None) 13 | fusionner = _Fusionner(painter, title) 14 | return State(fusionner) 15 | 16 | 17 | class State(_GhostState): 18 | 19 | """ 20 | Triplet of fusionner, position and ghost rect. 21 | Note that the ghost rect must be an inflation of the fusionner's one, i.e 22 | they share a common center coordinate. 23 | """ 24 | 25 | def __init__(self, fusionner, topleft=(0, 0), inflation=(0, 0)): 26 | """inflation is negative if ghost is bigger than img""" 27 | super(State, self).__init__(topleft, (0, 0)) 28 | self.fusionner = fusionner 29 | self.inflation = inflation 30 | self.ghost_rect = self.get_ghost_from_fus() 31 | 32 | def compute_inflation(self): 33 | semi_infl = get_rel_pos_topleft(self.fusionner.rect, self.ghost_rect) 34 | return (2 * semi_infl[0], 2 * semi_infl[1]) 35 | 36 | def set_ghost_rect(self, topleft, size): 37 | _GhostState.set_ghost_rect(self, topleft, size) 38 | self.inflation = self.compute_inflation() 39 | 40 | def get_anti_inflation(self): 41 | return (-self.inflation[0], -self.inflation[1]) 42 | 43 | def get_ghost_from_fus(self): 44 | """Returns inflated fusionner rect according to self.inflation.""" 45 | return self.fusionner.rect.inflate(self.get_anti_inflation()) 46 | 47 | def set_topleft(self, topleft): 48 | """Refresh the ghost rect and the fusionner rect too.""" 49 | self.ghost_rect.topleft = topleft 50 | # replace fusionner into the center 51 | self.fusionner.rect.center = self.ghost_rect.center 52 | 53 | def refresh_ghost_rect(self): 54 | self.ghost_rect = self.get_ghost_from_fus() 55 | 56 | def recenter_fusionner(self): 57 | self.fusionner.rect.center = self.ghost_rect.center 58 | -------------------------------------------------------------------------------- /miscgui/textpacker.py: -------------------------------------------------------------------------------- 1 | from thorpy.miscgui.painterstyle import WRITER 2 | 3 | def pack_text(width, text, element=None, sep=" "): 4 | """Returns a string with new lines inserted in order to fit the specified 5 | width. 6 | Specify element arg if you want to use element's writer to calculate text 7 | size.""" 8 | if element: 9 | writer = element.get_title()._writer 10 | else: 11 | writer = WRITER() 12 | new_text = "" 13 | split_text = text.split(sep) 14 | current_width = 0 15 | for word in split_text: 16 | original_word = str(word) 17 | newline = False 18 | while "\n" in word: 19 | newline = True 20 | word = word.replace("\n","") 21 | word_width = writer.get_width(word) 22 | current_width += word_width 23 | if current_width > width: 24 | new_text += "\n" 25 | current_width = word_width 26 | final_word = original_word + sep 27 | if newline: 28 | current_width = 0 29 | new_text += final_word 30 | return new_text 31 | -------------------------------------------------------------------------------- /miscgui/utils.py: -------------------------------------------------------------------------------- 1 | from thorpy.miscgui.title import Title 2 | from thorpy.painting.painters.painter import Painter 3 | from thorpy.painting.fusionner import _Fusionner 4 | 5 | def get_void_state(): 6 | ## from thorpy.miscgui.title import Title 7 | ## from thorpy.painting.painter import Painter 8 | ## from thorpy.miscgui.fusionner import _Fusionner 9 | title = Title("") 10 | painter = Painter(None) 11 | fusionner = _Fusionner(painter, title) 12 | return State(fusionner) -------------------------------------------------------------------------------- /painting/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/painting/__init__.py -------------------------------------------------------------------------------- /painting/fusionner.py: -------------------------------------------------------------------------------- 1 | """Used to produce a graphical Element""" 2 | from pygame.rect import Rect 3 | 4 | from thorpy.miscgui import style 5 | from thorpy.miscgui.title import Title 6 | 7 | 8 | class _Fusionner(object): 9 | """Put painter and title together to produce an image, using painter's 10 | properties. 11 | """ 12 | 13 | def __init__(self, painter, title): 14 | self.painter = painter 15 | self.title = title 16 | self.img = self.get_fusion() 17 | self.rect = Rect((0, 0), self.painter.size) 18 | 19 | def scale_to_title(self, margins=None, center_title=True, 20 | refresh_title=True): 21 | margins = style.MARGINS if margins is None else margins 22 | size_txt = self.title.get_rect(True).size 23 | size = (size_txt[0] + 2*margins[0] + 1, 24 | size_txt[1] + 2*margins[1] + 1) 25 | self.painter.size = size 26 | self.refresh(center_title, refresh_title) 27 | 28 | def get_fusion(self, center_title=True, title=None): 29 | """Fusion the painter.img and the title.img and returns this fusion.""" 30 | if not title: 31 | title = self.title 32 | return self.painter.get_fusion(title, center_title) 33 | 34 | def get_hover_fusion(self, center_title=True, writer=None, color=None): 35 | """Returns images corresponding to self.title._lines, with as 36 | writer. Default arg writer=None, means that title's writer is used. 37 | """ 38 | color = style.COLOR_TXT_HOVER if color is None else color 39 | if not writer: 40 | writer = self.title._writer 41 | # 42 | pos = self.title._pos 43 | space = self.title._space 44 | align = self.title._align 45 | cut_word = self.title._cut_word 46 | lines = self.title._lines 47 | old_col = self.title._writer.color 48 | # 49 | writer.set_color(color) 50 | title = Title("", writer, pos, space, align, cut_word) 51 | title._lines = lines 52 | title._imgs = title._writer.get_imgs(lines) 53 | title._text = self.title._text 54 | img = self.get_fusion(center_title, title) 55 | writer.set_color(old_col) 56 | return img 57 | 58 | def refresh(self, center_title=True, refresh_title=True): 59 | """Fusion the painter.img and the title.img into self.img""" 60 | if refresh_title: 61 | self.title.refresh_imgs() 62 | self.painter.refresh_clip() 63 | self.img = self.get_fusion(center_title) 64 | self.refresh_rect() 65 | 66 | def refresh_rect(self): 67 | self.rect.size = self.img.get_size() 68 | 69 | 70 | class FusionnerText(_Fusionner): 71 | 72 | def __init__(self, title): 73 | self.title = title 74 | self.img = self.get_fusion() 75 | img_size = self.img.get_size() 76 | self.rect = Rect((0, 0), img_size) 77 | 78 | def get_hover_fusion(self, center_title=None, writer=None, color=None): 79 | color=style.COLOR_TXT_HOVER if color is None else color 80 | old_col = self.title._writer.color 81 | if len(self.title._lines) != len(self.title._imgs): 82 | raise Exception("title problem") 83 | self.title._writer.set_color(color) 84 | img = self.title.get_imgs()[0] #only simple text use it, not multisimple text 85 | self.title._writer.set_color(old_col) 86 | return img 87 | 88 | def get_fusion(self, center_title=None, writer=None):#writer unused for compatibilty reasons 89 | return self.title._imgs[0] 90 | 91 | def refresh(self, center_title=None, refresh_title=True): 92 | if refresh_title: 93 | self.title.refresh_imgs() 94 | self.img = self.get_fusion() 95 | self.refresh_rect() 96 | 97 | def scale_to_title(self, margins=None, center_title=True, 98 | refresh_title=True): 99 | pass 100 | -------------------------------------------------------------------------------- /painting/makeup.py: -------------------------------------------------------------------------------- 1 | """This module provides functions to add graphical effects on elements.""" 2 | 3 | from thorpy.elements._makeuputils._shadow import StaticShadow 4 | from thorpy.elements.hoverzone import HoverZone 5 | from thorpy.miscgui import style 6 | from thorpy.painting.painters.imageframe import ImageFrame 7 | 8 | def add_static_shadow(element, dict_args=None): 9 | """Adds a static shadow to . This means that the shadow is always 10 | the same, regardless of the state of the element. 11 | """ 12 | shadowel = StaticShadow(element) 13 | if dict_args: 14 | for key, value in iter(dict_args.items()): 15 | setattr(shadowel, key, value) 16 | shadowel.finish() 17 | 18 | def add_button_shadow(element, altitude=None, dict_args=None): 19 | altitude = style.SHADOW_ALTITUDE if altitude is None else altitude 20 | shadow = StaticShadow(element) 21 | shadow.target_altitude = altitude 22 | shadow.vertical = False 23 | shadow.sun_angle2 = 45. 24 | shadow.alpha_factor = 0.5 25 | if dict_args: 26 | for key, value in (dict_args.items()): 27 | setattr(shadowel, key, value) 28 | shadow.finish() 29 | 30 | def remove_shadow(element): 31 | element._shadow.unlink() 32 | 33 | def add_basic_help(element, text, wait_time=None, jail=None, shadow=False, 34 | rect=None): 35 | if rect is None: 36 | rect = element.get_help_rect() 37 | hoverzone = HoverZone(rect) 38 | hoverzone.finish() 39 | element.add_elements([hoverzone]) 40 | hoverzone.add_basic_help(text, wait_time, jail) 41 | if shadow: 42 | add_button_shadow(hoverzone._help_element) 43 | hoverzone._help_element._shadow.visible = False 44 | return hoverzone 45 | 46 | ##def set_image_normal(element, image): 47 | ## painter = ImageFrame(image) 48 | ## fusionner = 49 | ## 50 | ##def set_image_pressed(element, image): 51 | ## pass 52 | ## 53 | ##def set_image_hover(element, image): 54 | ## pass -------------------------------------------------------------------------------- /painting/painters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/painting/painters/__init__.py -------------------------------------------------------------------------------- /painting/painters/basicframe.py: -------------------------------------------------------------------------------- 1 | from pygame import Surface, SRCALPHA 2 | 3 | from thorpy.miscgui.functions import debug_msg 4 | from thorpy.miscgui import style 5 | from thorpy.painting.painters.painter import Painter 6 | 7 | class BasicFrame(Painter): 8 | # get_fusion() 9 | # get_surface() 10 | # draw() 11 | # personnals... 12 | 13 | def __init__(self, size=None, color=None, pressed=False, hovered=False, 14 | clip=None): 15 | color = style.DEF_COLOR if color is None else color 16 | Painter.__init__(self, size=size, clip=clip, pressed=pressed, 17 | hovered=hovered) 18 | self.color = color 19 | 20 | def draw(self): 21 | if len(self.color) == 3 or self.color[-1] == 255: 22 | if self.size[0] < 1: 23 | debug_msg("Width < 1, automatic resize.", self) 24 | self.size = (1, self.size[1]) 25 | if self.size[1] < 1: 26 | debug_msg("Height < 1, automatic resize.", self) 27 | self.size = (self.size[0], 1) 28 | surface = Surface(self.size).convert() 29 | elif len(self.color) == 4: 30 | surface = Surface(self.size, flags=SRCALPHA).convert_alpha() 31 | else: 32 | raise Exception("Invalid color attribut") 33 | surface.fill(self.color) 34 | return surface 35 | 36 | def get_surface(self): 37 | surface = self.draw() 38 | surface.set_clip(self.clip) 39 | return surface -------------------------------------------------------------------------------- /painting/painters/classicframe.py: -------------------------------------------------------------------------------- 1 | from pygame import Rect 2 | from pygame.draw import lines 3 | 4 | from thorpy.painting.painters.basicframe import BasicFrame 5 | from thorpy.miscgui import style 6 | from thorpy._utils.rectscomputing import get_top_coords, get_bottom_coords 7 | from thorpy._utils.colorscomputing import grow_color, normalize_color 8 | 9 | 10 | class ClassicFrame(BasicFrame): 11 | 12 | def __init__(self, size=None, color=None, pressed=False, dark=None, 13 | hovered=False, light=None, thick=1, clip="auto"): 14 | if clip == "auto": 15 | inflation = -2 * thick 16 | clip = (inflation, inflation) 17 | BasicFrame.__init__(self, 18 | size=size, 19 | color=color, 20 | clip=clip, 21 | pressed=pressed, 22 | hovered=hovered) 23 | self.dark = dark 24 | self.light = light 25 | self.thick = thick 26 | self.light = style.LIGHT_FACTOR if light is None else light 27 | self.dark = style.DARK_FACTOR if dark is None else dark 28 | ## if self.light is None: 29 | ## white = make_compatible(constants.WHITE, self.color) 30 | ## self.light = mid_color(self.color, white) 31 | if isinstance(self.light, float): 32 | self.light = normalize_color(grow_color(self.light, self.color)) 33 | ## if self.dark is None: 34 | ## black = make_compatible(constants.BLACK, self.color) 35 | ## self.dark = mid_color(self.color, black) 36 | if isinstance(self.dark, float): 37 | self.dark = normalize_color(grow_color(self.dark, self.color)) 38 | 39 | def blit_borders_on(self, surface): 40 | rect = Rect((0, 0), self.size) 41 | for x in range(0, self.thick): 42 | r = rect.inflate(-x, -x) 43 | tc = get_top_coords(r) 44 | bc = get_bottom_coords(r) 45 | if self.pressed: 46 | lines(surface, self.dark, False, tc, 1) 47 | lines(surface, self.light, False, bc, 1) 48 | else: 49 | lines(surface, self.light, False, tc, 1) 50 | lines(surface, self.dark, False, bc, 1) 51 | 52 | def draw(self): 53 | surface = BasicFrame.draw(self) 54 | self.blit_borders_on(surface) 55 | return surface 56 | 57 | def get_fusion(self, title, center_title): 58 | """Fusion the painter.img and the title.img and returns this fusion""" 59 | if self.pressed: 60 | if center_title is True: # center the title on the element rect 61 | title.center_on(self.size) 62 | title._pos = (title._pos[0], title._pos[1] + self.thick) 63 | # center_title is the topleft argument 64 | elif center_title is not False: 65 | title._pos = center_title 66 | else: 67 | title._pos = (0, 0) 68 | painter_img = self.get_surface() 69 | title.blit_on(painter_img) 70 | return painter_img 71 | else: 72 | return BasicFrame.get_fusion(self, title, center_title) 73 | 74 | def set_color(self, color): 75 | self.color = color 76 | if len(color) == 4: 77 | self.dark = tuple(list(self.dark) + [color[3]]) 78 | self.light = tuple(list(self.light) + [color[3]]) 79 | -------------------------------------------------------------------------------- /painting/painters/classicround.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | from pygame import Surface 4 | from pygame.constants import SRCALPHA 5 | from pygame.gfxdraw import arc as arc # to change if pygame changes 6 | # to change if pygame changes: 7 | from pygame.gfxdraw import filled_circle as filled_circle 8 | 9 | from thorpy._utils.colorscomputing import mid_color, make_compatible 10 | from thorpy.painting.painters.painter import Painter 11 | from thorpy.miscgui import constants, style 12 | 13 | 14 | class ClassicRound(Painter): 15 | 16 | def __init__(self, size=None, color=None, pressed=False, dark=None, 17 | hovered=False, light=None, thick=1, clip=None): 18 | """Size : diameter""" 19 | size = style.CHECK_SIZE if size is None else size 20 | color = style.DEF_COLOR2 if color is None else color 21 | Painter.__init__(self, size=size, clip=clip, pressed=pressed, 22 | hovered=hovered) 23 | self.color = color 24 | self.dark = dark 25 | self.light = light 26 | self.thick = thick 27 | 28 | def blit_borders_on(self, surface): 29 | if not self.light: 30 | white = make_compatible(constants.WHITE, self.color) 31 | self.light = mid_color(self.color, white) 32 | if not self.dark: 33 | black = make_compatible(constants.BLACK, self.color) 34 | self.dark = mid_color(self.color, black) 35 | ## rect = Rect((0, 0), self.size) 36 | x = y = r = self.size[0] // 2 37 | r -= 2 38 | filled_circle(surface, x, y, r, self.color) 39 | ## circle(surface, x, y, r, self.dark) 40 | arc(surface, x, y, r, 135, 135 + 180, self.dark) 41 | arc(surface, x, y, r, 135 + 180, 135 + 360, self.light) 42 | 43 | def draw(self): 44 | surface = Surface(self.size, flags=SRCALPHA).convert_alpha() 45 | surface.fill(constants.TRANSPARENT) 46 | self.blit_borders_on(surface) 47 | return surface 48 | 49 | def get_surface(self): 50 | surface = self.draw() 51 | surface.set_clip(self.clip) 52 | return surface 53 | -------------------------------------------------------------------------------- /painting/painters/inserterframe.py: -------------------------------------------------------------------------------- 1 | from pygame import Surface, Rect, SRCALPHA 2 | 3 | from thorpy.painting.painters.classicframe import ClassicFrame 4 | from thorpy.miscgui import style 5 | 6 | # get_fusion() 7 | # get_surface() 8 | # draw() 9 | # personnals... 10 | 11 | 12 | class InserterFrame(ClassicFrame): 13 | 14 | def __init__(self, size=None, color=None, pressed=False, space=8, dark=None, 15 | light=None, thick=1, clip="auto"): 16 | size = style.XLARGE_SIZE if size is None else size 17 | color = style.DEF_COLOR2 if color is None else color 18 | ClassicFrame.__init__(self, 19 | size=size, 20 | color=color, 21 | pressed=pressed, 22 | hovered=hovered, 23 | dark=dark, 24 | light=light, 25 | thick=thick, 26 | clip=clip) 27 | self.space = space 28 | self.txt_zone = None 29 | 30 | def get_fusion(self, title, center_title=None, hover=False): 31 | """Fusion the painter.img and the title.img and returns this fusion. 32 | center_title is ignored.""" 33 | title.center_on(self.size) 34 | title._pos = (0, title._pos[1]) 35 | title_length = title.img.get_size()[0] 36 | painter_img = self.get_surface(title_length) 37 | ## painter_img.blit(title.img, title._pos) 38 | title.blit_on(painter_img) 39 | return painter_img 40 | 41 | def get_surface(self, title_length): 42 | surface = self.draw(title_length) 43 | surface.set_clip(self.clip) 44 | return surface 45 | 46 | def draw(self, title_length): 47 | # actual surface (transparent): 48 | surface = Surface(self.size, flags=SRCALPHA).convert_alpha() 49 | surface.fill((0, 0, 0, 0)) 50 | # computing frame length: tot = title + space + frame 51 | frame_length = self.size[0] - title_length - self.space 52 | frame_size = (frame_length, self.size[1]) 53 | self.txt_zone = Rect((title_length + self.space, 0), frame_size) 54 | frame_painter = ClassicFrame(frame_size, self.color, True, self.dark, 55 | self.light, self.thick) 56 | # frame in which text will be inserted: 57 | frame = frame_painter.get_surface() 58 | surface.blit(frame, (title_length + self.space, 0)) 59 | return surface 60 | -------------------------------------------------------------------------------- /painting/painters/optionnal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YannThorimbert/Thorpy/e9dc52ff209f684be578f57a324f5bcf29d536ad/painting/painters/optionnal/__init__.py -------------------------------------------------------------------------------- /painting/painters/optionnal/_deprecated_grid.py: -------------------------------------------------------------------------------- 1 | from pygame.draw import rect as draw_rect 2 | from pygame import Rect 3 | 4 | from thorpy.painting.painters.basicframe import BasicFrame 5 | from thorpy._utils.colorscomputing import normalize_color, grow_color 6 | from thorpy.miscgui import style 7 | 8 | from thorpy.gamestools.grid import Grid 9 | 10 | 11 | class GridPainter(BasicFrame): 12 | 13 | def __init__(self, size=style.SIZE, color=style.DEF_COLOR, pressed=False, 14 | hovered=False, clip="auto", dark=None, light=None, thick=2): 15 | if clip is "auto": 16 | inflation = -2 * thick 17 | clip = (inflation, inflation) 18 | BasicFrame.__init__(self, 19 | size=size, 20 | color=color, 21 | clip=clip, 22 | pressed=pressed, 23 | hovered=hovered) 24 | self.light = style.LIGHT_FACTOR if light is None else light 25 | self.dark = style.DARK_FACTOR if dark is None else dark 26 | if isinstance(self.light, float): 27 | self.light = normalize_color(grow_color(self.light, self.color)) 28 | if isinstance(self.dark, float): 29 | self.dark = normalize_color(grow_color(self.dark, self.color)) 30 | self.thick = thick 31 | 32 | def draw(self): 33 | surface = BasicFrame.draw(self) 34 | if not self.pressed: 35 | draw_rect(surface, self.dark, Rect((0, 0), self.size), self.thick) 36 | else: 37 | draw_rect(surface, self.light, Rect((0, 0), self.size), self.thick) 38 | return surface 39 | 40 | def get_surface(self): 41 | surface = self.draw() 42 | surface.set_clip(self.clip) 43 | return surface 44 | -------------------------------------------------------------------------------- /painting/painters/optionnal/grid.py: -------------------------------------------------------------------------------- 1 | from pygame.draw import rect as draw_rect 2 | from pygame import Rect 3 | 4 | from thorpy.painting.painters.basicframe import BasicFrame 5 | from thorpy._utils.colorscomputing import normalize_color, grow_color 6 | from thorpy.miscgui import style 7 | 8 | from thorpy.gamestools.grid import Grid 9 | 10 | 11 | class GridPainter(BasicFrame): 12 | 13 | def __init__(self, size=None, color=None, pressed=False, 14 | hovered=False, clip="auto", dark=None, light=None, thick=2): 15 | if size is None: size=style.SIZE 16 | if color is None: size=style.DEF_COLOR 17 | if clip is "auto": 18 | inflation = -2 * thick 19 | clip = (inflation, inflation) 20 | BasicFrame.__init__(self, 21 | size=size, 22 | color=color, 23 | clip=clip, 24 | pressed=pressed, 25 | hovered=hovered) 26 | self.light = style.LIGHT_FACTOR if light is None else light 27 | self.dark = style.DARK_FACTOR if dark is None else dark 28 | if isinstance(self.light, float): 29 | self.light = normalize_color(grow_color(self.light, self.color)) 30 | if isinstance(self.dark, float): 31 | self.dark = normalize_color(grow_color(self.dark, self.color)) 32 | self.thick = thick 33 | 34 | def draw(self): 35 | surface = BasicFrame.draw(self) 36 | if not self.pressed: 37 | draw_rect(surface, self.dark, Rect((0, 0), self.size), self.thick) 38 | else: 39 | draw_rect(surface, self.light, Rect((0, 0), self.size), self.thick) 40 | return surface 41 | 42 | def get_surface(self): 43 | surface = self.draw() 44 | surface.set_clip(self.clip) 45 | return surface 46 | -------------------------------------------------------------------------------- /painting/painters/optionnal/illuminer.py: -------------------------------------------------------------------------------- 1 | from thorpy.painting.painters.basicframe import BasicFrame 2 | from thorpy.miscgui import constants, functions, style 3 | from thorpy.painting.graphics import illuminate_alphacolor_except 4 | 5 | 6 | class IlluminerAlphaExcept(BasicFrame): 7 | """Illuminer that also set alpha values, in addition to colors. 8 | All pixels with RGB value will be taken into account for fading, 9 | while all other pixels will be considered as light source. 10 | """ 11 | 12 | def __init__(self, size=None, colorkey=constants.WHITE, clip=None, 13 | color_target=None, color_bulk=None, subrect=None, 14 | pressed=False, hovered=False, factor=1., fadout=10., bulk_alpha=255): 15 | if size is None: size=style.SIZE 16 | if color_target is None: color_target=style.DEF_COLOR 17 | BasicFrame.__init__(self, 18 | size=size, 19 | color=colorkey, 20 | clip=clip, 21 | pressed=pressed, 22 | hovered=hovered) 23 | self.color_target = color_target 24 | self.color_bulk = color_bulk 25 | self.subrect = subrect 26 | self.factor = factor 27 | self.fadout = fadout 28 | self.bulk_alpha = bulk_alpha 29 | 30 | 31 | def get_fusion(self, title, center_title): 32 | """Fusion the painter.img and the title.img and returns this fusion""" 33 | if center_title is True: # center the title on the element rect 34 | title.center_on(self.size) 35 | elif center_title is not False: # center_title is the topleft argument 36 | title._pos = center_title 37 | else: 38 | title._pos = (0, 0) 39 | painter_img = self.get_surface() 40 | title.blit_on(painter_img) 41 | functions.debug_msg("Building illuminer of size " + str(self.size)) 42 | return illuminate_alphacolor_except(painter_img, self.color, 43 | self.color_target, self.color_bulk, 44 | self.subrect, self.factor, 45 | self.fadout, self.bulk_alpha) 46 | 47 | class IlluminerAlphaText(IlluminerAlphaExcept): 48 | """Text-specialized Illuminer that also set alpha values, in addition to 49 | colors. 50 | All pixels with RGB value will be taken into account for fading, 51 | while all other pixels will be considered as light source. 52 | """ 53 | 54 | def get_fusion(self, title, center_title): 55 | """Fusion the painter.img and the title.img and returns this fusion""" 56 | if title._writer.color == self.color: 57 | functions.debug_msg("Colorkey is the same as writer's color while\ 58 | generating " + title._text) 59 | if center_title is True: # center the title on the element rect 60 | title.center_on(self.size) 61 | elif center_title is not False: # center_title is the topleft argument 62 | title._pos = center_title 63 | else: 64 | title._pos = (0, 0) 65 | painter_img = self.get_surface() 66 | old_aa = title._writer.aa 67 | old_imgs = title._imgs 68 | if old_aa: 69 | title._writer.aa = False 70 | title.refresh_imgs() 71 | title.blit_on(painter_img) 72 | if old_aa: 73 | title._writer.aa = True 74 | title._imgs = old_imgs 75 | functions.debug_msg("Building illuminer of size " + str(self.size)) 76 | return illuminate_alphacolor_except(painter_img, self.color, 77 | self.color_target, self.color_bulk, 78 | self.subrect, self.factor, 79 | self.fadout, self.bulk_alpha) 80 | -------------------------------------------------------------------------------- /painting/painters/optionnal/rectframe.py: -------------------------------------------------------------------------------- 1 | from pygame.draw import rect as draw_rect 2 | from pygame import Rect 3 | 4 | from thorpy.painting.painters.basicframe import BasicFrame 5 | from thorpy._utils.colorscomputing import normalize_color, grow_color 6 | from thorpy.miscgui import style 7 | 8 | 9 | class RectFrame(BasicFrame): 10 | 11 | def __init__(self, size=None, color=None, clip="auto", 12 | dark=None, light=None, thick=2, pressed=False, hover=False): 13 | if size is None: size=style.SIZE 14 | if color is None: color=style.DEF_COLOR 15 | if clip is "auto": 16 | inflation = -2 * thick 17 | clip = (inflation, inflation) 18 | BasicFrame.__init__(self, 19 | size=size, 20 | color=color, 21 | clip=clip, 22 | pressed=pressed, 23 | hovered=hover) 24 | self.light = style.LIGHT_FACTOR if light is None else light 25 | self.dark = style.DARK_FACTOR if dark is None else dark 26 | if isinstance(self.light, float): 27 | self.light = normalize_color(grow_color(self.light, self.color)) 28 | if isinstance(self.dark, float): 29 | self.dark = normalize_color(grow_color(self.dark, self.color)) 30 | self.thick = thick 31 | 32 | def draw(self): 33 | surface = BasicFrame.draw(self) 34 | if not self.pressed: 35 | draw_rect(surface, self.dark, Rect((0, 0), self.size), self.thick) 36 | else: 37 | draw_rect(surface, self.light, Rect((0, 0), self.size), self.thick) 38 | return surface 39 | 40 | def get_surface(self): 41 | surface = self.draw() 42 | surface.set_clip(self.clip) 43 | return surface 44 | 45 | class Windows10Frame(RectFrame): 46 | def __init__(self, size=None, color=None, clip="auto", dark=(20,20,20), 47 | light=None, thick=3, pressed=False, hover=False): 48 | RectFrame.__init__(self, size, color, clip, dark, light, thick, pressed, hover) 49 | 50 | def draw(self): 51 | surface = BasicFrame.draw(self) 52 | if self.pressed: 53 | draw_rect(surface, self.dark, Rect((0, 0), self.size), self.thick+2) 54 | elif self.hovered: 55 | draw_rect(surface, self.dark, Rect((0, 0), self.size), self.thick) 56 | return surface 57 | 58 | -------------------------------------------------------------------------------- /painting/painters/optionnal/rectreflect.py: -------------------------------------------------------------------------------- 1 | from thorpy.painting.painters.classicframe import ClassicFrame 2 | from thorpy.miscgui import style 3 | from thorpy.painting.graphics import linear_v_monogradation 4 | 5 | 6 | class RectReflectFrame(ClassicFrame): 7 | 8 | def __init__(self, size=None, color=None, pressed=False, 9 | dark=None, light=None, thick=1, clip="auto", 10 | ambient=None, hfact=0.6): 11 | if size is None: size=style.SIZE 12 | if color is None: color=style.DEF_COLOR 13 | if ambient is None: ambient = style.DEF_COLOR3 14 | self.thick = 1 15 | if clip is "auto": 16 | inflation = -2 * thick 17 | clip = (inflation, inflation) 18 | ClassicFrame.__init__(self, size, color, pressed, dark, light, thick, 19 | clip) 20 | self.ambient = ambient 21 | self.hfact = hfact 22 | 23 | def draw(self): 24 | surface = ClassicFrame.draw(self) 25 | h = int(self.hfact*self.size[1]) 26 | linear_v_monogradation(surface, 1, h, self.light, self.ambient, 1) 27 | linear_v_monogradation(surface, h+1, self.size[1], self.dark, self.color, 1) 28 | return surface 29 | 30 | def get_surface(self): 31 | surface = self.draw() 32 | surface.set_clip(self.clip) 33 | return surface 34 | -------------------------------------------------------------------------------- /painting/painters/painter.py: -------------------------------------------------------------------------------- 1 | from pygame.rect import Rect 2 | 3 | from thorpy.miscgui import style 4 | from thorpy.miscgui.functions import debug_msg 5 | 6 | 7 | class Painter(object): 8 | """Mother class for all painters. Not to be instanciated. 9 | Any Painter must have the following attributes: 10 | - size (a 2-tuple) 11 | - clip, can either be: 12 | 1) a couple : (a, b) with a and b the x and y values for the 13 | inflation of the surface's size. E.g, (-3, -3). 14 | 15 | 2) a 4-uple : (a, b, c, d) with a and b the topleft of the rect and 16 | c and d its size. It creates a pygame.Rect to be used for the 17 | clipping of the surface. 18 | 19 | 3) else : no clip will be used. 20 | - pressed : boolean 21 | - hovered : boolean 22 | """ 23 | 24 | def __init__(self, size=None, clip=None, pressed=False, hovered=False): 25 | size = style.SIZE if size is None else size 26 | w, h = size 27 | w = int(w) 28 | h = int(h) 29 | if w < 0: 30 | w = 0 31 | debug_msg("Painter width was negative. Set to 0.") 32 | if h < 0: 33 | h = 0 34 | debug_msg("Painter height was negative. Set to 0.") 35 | self.size = (w, h) 36 | self.clip = self.treat_clip(clip) 37 | self.original_clip = clip 38 | self.pressed = pressed 39 | self.hovered = hovered 40 | 41 | def set_size(self, size): 42 | self.size = size 43 | 44 | def treat_clip(self, clip): 45 | """ 46 | Get as argument and returns a Rect suitable for use with pygame 47 | surface's set_clip method. 48 | 49 | can either be: 50 | 1) a couple : (a, b) with a and b the x and y values for the 51 | inflation of the surface's size. E.g, (-3, -3). 52 | 53 | 2) a 4-uple : (a, b, c, d) with a and b the topleft of the rect and 54 | c and d its size. It creates a pygame.Rect to be used for the 55 | clipping of the surface. 56 | 57 | 3) else : no clip will be used. 58 | """ 59 | if clip: 60 | if len(clip) == 2: # then is the inflation of the rect 61 | return Rect((0, 0), self.size).inflate(clip) 62 | # then is the topleft, size args of the rect 63 | elif len(clip) == 4: 64 | return Rect(clip[0], clip[1], clip[2], clip[3]) 65 | return None # then is the whole frame 66 | 67 | def set_clip(self, new_clip): 68 | self.clip = self.treat_clip(new_clip) 69 | 70 | def get_clip(self): 71 | return self.clip 72 | 73 | def refresh_clip(self): 74 | self.clip = self.treat_clip(self.original_clip) 75 | if self.clip and len(self.original_clip) == 4: 76 | debug_msg("Attention : painter is refreshing, but original clip is\ 77 | not an inflation or 'None'.") 78 | 79 | def get_surface(self): 80 | pass 81 | 82 | def get_fusion(self, title, center_title): 83 | """Fusion the painter.img and the title.img and returns this fusion""" 84 | if center_title is True: # center the title on the element rect 85 | title.center_on(self.size) 86 | elif center_title is not False: # center_title is the topleft argument 87 | title._pos = center_title 88 | else: 89 | title._pos = (0, 0) 90 | painter_img = self.get_surface() 91 | ## painter_img.blit(title.img, title._pos) 92 | title.blit_on(painter_img) 93 | return painter_img 94 | 95 | def set_color(self, color): 96 | self.color = color 97 | -------------------------------------------------------------------------------- /painting/painters/roundrect.py: -------------------------------------------------------------------------------- 1 | from pygame import SRCALPHA, BLEND_RGBA_MAX, BLEND_RGBA_MIN, Rect, Color 2 | from pygame import Surface, draw, transform 3 | 4 | from thorpy.painting.painters.basicframe import BasicFrame 5 | from thorpy.miscgui import style 6 | 7 | 8 | class RoundRect(BasicFrame): 9 | 10 | def __init__(self, size=None, color=None, clip=None, 11 | radius=0.): 12 | """If radius is in the range [0, 1], self.radius_value is the fraction 13 | of radius * min(size), else it is interpreted as a raw pixel value. 14 | """ 15 | BasicFrame.__init__(self, size, color, clip) 16 | self.radius_value = style.DEF_RADIUS if radius is None else radius 17 | if 0. <= radius <= 1.: 18 | self.radius_value = min(self.size) * radius 19 | 20 | ## def draw(self): 21 | ## from pygame import gfxdraw as gfx 22 | ## surface = Surface(self.size, flags=SRCALPHA).convert_alpha() 23 | ## surface.fill((0,0,0,0)) 24 | ## rect = surface.get_rect() 25 | ## radius = int(self.radius * min(self.size)) 26 | ## intern_rect = rect.inflate((-2*radius, -2*radius)) 27 | ## gfx.filled_circle(surface, intern_rect.left, intern_rect.top, radius, self.color) 28 | ## gfx.filled_circle(surface, intern_rect.right, intern_rect.top, radius, self.color) 29 | ## gfx.filled_circle(surface, intern_rect.left, intern_rect.bottom, radius, self.color) 30 | ## gfx.filled_circle(surface, intern_rect.right, intern_rect.bottom, radius, self.color) 31 | #### draw.rect(surface, self.color, intern_rect) 32 | ## draw.rect(surface, self.color, Rect(0, radius, radius, rect.h - 2*radius)) 33 | #### draw.rect(surface, self.color, intern_rect.inflate(2*radius, 0)) 34 | ## return surface 35 | 36 | def draw(self): 37 | surface = Surface(self.size, flags=SRCALPHA).convert_alpha() 38 | rect = Rect((0, 0), self.size) 39 | color = Color(*self.color) 40 | alpha = color.a 41 | color.a = 0 42 | rectangle = Surface(rect.size, SRCALPHA) 43 | #ex: [h*3, h*3] 44 | circle = Surface([min(rect.size) * 5] * 2, SRCALPHA) 45 | draw.ellipse(circle, (0, 0, 0), circle.get_rect(), 0) 46 | #ex: [h*0.5, h*.05] 47 | circle = transform.smoothscale(circle, 48 | [int(self.radius_value)] * 2) 49 | #now circle is just a small circle of radius self.radius*h (for example) 50 | #blit topleft circle: 51 | radius = rectangle.blit(circle, (0, 0)) 52 | #now radius = Rect((0, 0), circle.size), rect=Rect((0, 0), self.size) 53 | #blit bottomright circle: 54 | radius.bottomright = rect.bottomright #radius is growing 55 | rectangle.blit(circle, radius) 56 | #blit topright circle: 57 | radius.topright = rect.topright 58 | rectangle.blit(circle, radius) 59 | #blit bottomleft circle: 60 | radius.bottomleft = rect.bottomleft 61 | rectangle.blit(circle, radius) 62 | #black-fill of the internal rect 63 | rectangle.fill((0, 0, 0), rect.inflate(-radius.w, 0)) 64 | rectangle.fill((0, 0, 0), rect.inflate(0, -radius.h)) 65 | #fill with color using blend_rgba_max 66 | rectangle.fill(color, special_flags=BLEND_RGBA_MAX) 67 | #fill with alpha-withe using blend_rgba_min in order to make transparent 68 | #the 69 | rectangle.fill((255, 255, 255, alpha), special_flags=BLEND_RGBA_MIN) 70 | surface.blit(rectangle, rect.topleft) 71 | return surface 72 | -------------------------------------------------------------------------------- /painting/pilgraphics.py: -------------------------------------------------------------------------------- 1 | """Module for producing graphics using Python Imaging Library. 2 | By convention, all the functions return PIL images, unless the name of the 3 | function indicates that this is not the case. 4 | """ 5 | 6 | try: 7 | from PIL import Image, ImageFilter 8 | HAS_PIL = True 9 | except ImportError: 10 | HAS_PIL = False 11 | 12 | try: 13 | import numpy 14 | import pygame.surfarray as surfarray 15 | HAS_NUMPY = True 16 | except ImportError: 17 | HAS_NUMPY = False 18 | 19 | from pygame.image import tostring, fromstring 20 | from pygame import Surface, SRCALPHA 21 | 22 | from thorpy._utils.colorscomputing import square_color_norm 23 | 24 | MAX_NORM = 3*(255**2) 25 | 26 | def pygame_surf_to_pil_img(surf, color_format="RGBA"): 27 | if not HAS_PIL: 28 | raise Exception("PIL was not found on this machine.") 29 | size = surf.get_size() 30 | pil_string_image = tostring(surf, color_format, False) 31 | return Image.frombytes(color_format, size, pil_string_image) 32 | 33 | def pil_img_to_pygame_surf(img, color_format="RGBA"): 34 | if not HAS_PIL: 35 | raise Exception("PIL was not found on this machine.") 36 | size = img.size 37 | data = img.convert(color_format).tobytes("raw", color_format) 38 | return fromstring(data, size, color_format) 39 | 40 | def get_black_white(surf, black=128, color_format="RGBA", convert=True): 41 | if not HAS_PIL: 42 | raise Exception("PIL was not found on this machine.") 43 | img = pygame_surf_to_pil_img(surf) 44 | gray = img.convert('L') 45 | bw = gray.point(lambda x: 0 if x