Devine-dessin/test_kivy_draw.py

348 lines
13 KiB
Python
Executable File

#!/usr/bin/env python3
from random import random, randint, choice
from datetime import timedelta
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button, ButtonBehavior
from kivy.uix.label import Label
from kivy.graphics import Color, Ellipse, Line, Rectangle
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.uix.popup import Popup
from kivy.uix.colorpicker import ColorPicker
from kivy.uix.scrollview import ScrollView
from kivy.properties import StringProperty, BooleanProperty
from kivy.lang import Builder
PASS_PENALTY = 4
DRAWING_TIME = 60
Builder.load_string('''
<ScrollableLabel>:
Label:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
text: root.text
markup: root.markup
''')
class ScrollableLabel(ScrollView):
text = StringProperty('')
markup = BooleanProperty(False)
class MyPaintWidget(Label):
_game_state = {"game_started": False,
"game_paused": False,
"game_finished": False,
"word passed": timedelta(0),
"word found": timedelta(0)
}
_game_wordlists = {"possible_wordlist": set(),
"found_words": set(),
"unfound_words": set()
}
pen_characteristics = {"current_color": (0,0,0,1),
"current_width": 4
}
current_word = ""
message_label = None
time = timedelta(seconds=DRAWING_TIME)
_popup = None
def __init__(self, text="", **kwargs):
super().__init__(**kwargs)
self._keyboard = Window.request_keyboard(
self._keyboard_closed, self, 'text')
if self._keyboard.widget:
# If it exists, this widget is a VKeyboard object which you can use
# to change the keyboard layout.
pass
self._keyboard.bind(on_key_down=self._on_keyboard_down)
with open("words.txt", "r") as wordfile:
self._wordlist = (word.strip() for word in wordfile.readlines())
self._game_wordlists["possible_wordlist"] = set(self._wordlist)
self.reset_game()
def _keyboard_closed(self):
print('My keyboard has been closed!')
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
print('The key', keycode, 'have been pressed')
print(' - text is %r' % text)
print(' - modifiers are %r' % modifiers)
if not self._game_state["game_started"]:
if keycode[0] == 32:
self.start_game()
elif keycode[1] == 'p':
self.toggle_pause()
elif self.is_game_playing:
if keycode[0] == 32:
self.next_word()
elif keycode[0] == 13:
self.pass_word()
elif keycode[1] == 'z' and "ctrl" in modifiers:
if self.canvas.before.children:
self.canvas.before.remove(self.canvas.before.children[-1])
self.canvas.before.remove(self.canvas.before.children[-1])
self.canvas.before.remove(self.canvas.before.children[-1])
elif self._game_state["game_finished"]:
if keycode[0] == 8:
self.reset_game()
# Keycode is composed of an integer + a string
# If we hit escape, let the input through to leave the app
if keycode[1] == 'escape':
return False
# Return True to accept the key. Otherwise, it will be used by
# the system.
return True
def toggle_pause(self):
self._game_state["game_paused"] = not self._game_state["game_paused"]
def start_game(self):
self.text=""
self._game_state["game_started"] = True
self._game_state["game_paused"] = False
self.next_word(True)
def next_word(self, passed=False):
if not passed:
self._game_wordlists["found_words"].add(self.current_word)
if self.message_label and self.current_word:
if passed:
self.message_label.color = '#FF2A40'
self.message_label.text = f'"{self.current_word}" a été passé !'
else:
self.message_label.color = '#5BB834'
self.message_label.text = f'"{self.current_word}" a été trouvé !'
self.current_word = choice(list(self._game_wordlists["possible_wordlist"]))
self._game_wordlists["possible_wordlist"].discard(self.current_word)
self.clear_drawing()
def pass_word(self):
self._game_wordlists["unfound_words"].add(self.current_word)
self.next_word(True)
if self.time - timedelta(seconds=PASS_PENALTY) > timedelta(0):
self.time -= timedelta(seconds=PASS_PENALTY)
else:
self.time = timedelta(0)
@property
def is_game_playing(self):
return (self._game_state["game_started"]
and not self._game_state["game_paused"]
and not self._game_state["game_finished"])
self.current_word = choice(list(self._game_wordlists["possible_wordlist"]))
self._game_wordlists["possible_wordlist"].discard(self.current_word)
self.clear_drawing()
def pass_word(self):
self._game_wordlists["unfound_words"].add(self.current_word)
self.next_word(True)
if self.time - timedelta(seconds=PASS_PENALTY) > timedelta(0):
self.time -= timedelta(seconds=PASS_PENALTY)
else:
self.time = timedelta(0)
@property
def is_game_playing(self):
return (self._game_state["game_started"]
and not self._game_state["game_paused"]
and not self._game_state["game_finished"])
@property
def current_color(self):
return self.pen_characteristics["current_color"]
def on_touch_down(self, touch):
if self.is_game_playing:
with self.canvas.before:
if self.collide_point(touch.x, touch.y):
Color(*self.pen_characteristics["current_color"], mode='rgba')
diameter = self.pen_characteristics["current_width"]
Ellipse(pos=(touch.x - diameter / 2, touch.y - diameter / 2),
size=(2*diameter, 2*diameter))
touch.ud['line'] = Line(points=(touch.x, touch.y), width=diameter)
def on_touch_move(self, touch):
if self.is_game_playing:
if 'line' in touch.ud:
if self.collide_point(touch.x, touch.y):
touch.ud['line'].points += [touch.x, touch.y]
elif self.collide_point(touch.ud['line'].points[-2], touch.y):
touch.ud['line'].points += [touch.ud['line'].points[-2], touch.y]
elif self.collide_point(touch.x, touch.ud['line'].points[-1]):
touch.ud['line'].points += [touch.x, touch.ud['line'].points[-1]]
def recap(self):
found_words = self._game_wordlists['found_words']
unfound_words =self._game_wordlists['unfound_words']
lines = ["[b]Tour terminé ![/b]\n"+
f"Trouvés: [color=#5BB834]{len(found_words)}[/color]",
"-"+ "\n-".join(found_words) if found_words else "",
f"Passés: [color=#FF2A40]{len(unfound_words)}[/color]",
"- "+ "\n- ".join(unfound_words) if unfound_words else ""]
self.clear_drawing()
self._popup = Popup(title='Fini !',
content=ScrollableLabel(text="[size=24]"+"\n".join(lines)+"[/size]",
markup=True),
size_hint=(0.6,0.6),
auto_dismiss=False)
self._popup.open()
def game_finished(self):
self._game_state["game_finished"] = True
self.recap()
def reset_game(self):
if self._popup:
self._popup.dismiss()
self.color = (0,0,0,1)
self.text = "Appuyer sur 'espace' pour commencer à jouer"
self.time = timedelta(seconds=DRAWING_TIME)
self._game_state["game_finished"] = False
self._game_state["game_started"] = False
def clear_drawing(self):
self.canvas.before.clear()
class SizeButton(ButtonBehavior, Widget):
def __init__(self, callback, size=1, **kwargs):
super().__init__(**kwargs)
self.dot_size = size
self.callback = callback
with self.canvas:
Color(0,0,0, mode="rgb")
self.bg = Rectangle(pos=self.pos, size=self.size)
Color(1,1,1, mode="rgb")
self.dot = Ellipse(pos=(self.center_x-size/2, self.center_y-size/2),
size = (size*2, size*2))
def redraw(self, args):
self.dot.pos = (self.center_x-size/2, self.center_y-size/2)
self.bg.pos = self.pos
self.bg.size = self.size
self.bind(size=redraw, pos=redraw)
def on_release(self):
self.callback(self.dot_size)
class MyPaintApp(App):
state = False
init = True
def build(self):
Window.clearcolor = (1, 1, 1, 1)
#self.init_keyboard()
parent = BoxLayout(orientation='vertical')
self.painter = MyPaintWidget(size_hint=(0.9, 1))
Clock.schedule_interval(self.update_time, 1.0/10.0)
clearbtn = Button(text='Clear')
clearbtn.bind(on_release=self.clear_canvas)
togglebtn = Button(text='Inverser la couleur du fond')
togglebtn.bind(on_release=self.toggle_background)
self.right_row = BoxLayout(orientation="vertical", size_hint=(0.1,1))
color_picker_button = Button(text="Couleur")
color_picker_button.background_color = self.painter.pen_characteristics["current_color"]
color_picker_button.background_normal = ''
color_picker_button.bind(on_release=self.open_color_picker)
self.right_row.add_widget(color_picker_button)
for i in range(1,6):
size_button = SizeButton(self.set_cursor_size, 2*i)
self.right_row.add_widget(size_button)
middle_row = BoxLayout(orientation="horizontal", size_hint=(1, 0.8))
middle_row.add_widget(self.painter)
middle_row.add_widget(self.right_row)
bottom_row = BoxLayout(orientation='horizontal', spacing=5, size_hint=(1, .1))
bottom_row.add_widget(togglebtn)
bottom_row.add_widget(clearbtn)
top_row = BoxLayout(orientation='horizontal', spacing=5, size_hint=(1, .1))
self.word_label = Label(text="Mot:", color=[0,0,0,1], halign="left", valign="top")
def redraw(self, obj):
self.text_size = obj
self.word_label.bind(size=redraw)
self.time_label = Label(text=f"{self.painter.time}", color=[0,0,0,1])
message_label = Label(color=[0,0,0,1])
top_row.add_widget(self.word_label)
top_row.add_widget(message_label)
top_row.add_widget(self.time_label)
self.painter.message_label = message_label
parent.add_widget(top_row)
parent.add_widget(middle_row)
parent.add_widget(bottom_row)
return parent
def set_cursor_size(self, size):
print(size)
self.painter.pen_characteristics["current_width"] = size
def open_color_picker(self, _):
clr_picker = ColorPicker()
clr_picker.color = self.painter.pen_characteristics["current_color"]
def on_color_change(instance, value):
self.painter.pen_characteristics["current_color"] = value
self.right_row.children[-1].background_color = self.painter.current_color
clr_picker.bind(color=on_color_change)
popup = Popup(title="Couleur du pinceau",
content=clr_picker,
size_hint=(0.5,0.5))
popup.open()
def update_time(self, _):
if self.painter.is_game_playing:
if self.painter.time > timedelta(0):
self.painter.time -= timedelta(milliseconds=100)
self.word_label.text = f"Mot: {self.painter.current_word}"
else:
self.painter.game_finished()
self.time_label.text = f"{self.painter.time}"
def toggle_background(self, obj):
if self.state:
Window.clearcolor = (1, 1, 1, 1)
self.painter.pen_characteristics["current_color"] = (0,0,0,1)
self.right_row.children[-1].background_color = self.painter.current_color
self.word_label.color = (0,0,0,1)
self.time_label.color = (0,0,0,1)
self.state = False
else:
Window.clearcolor = (0,0,0,1)
self.painter.pen_characteristics["current_color"] = (1,1,1,1)
self.right_row.children[-1].background_color = self.painter.current_color
self.word_label.color = (1,1,1,1)
self.time_label.color = (1,1,1,1)
self.state = True
self.clear_canvas(obj)
def clear_canvas(self, _):
self.painter.clear_drawing()
if __name__ == '__main__':
MyPaintApp().run()