I'm working on a python paint project using kivy but I really cant export the png file, and when I can in the save and exit button (quitter et sauvegarder) on it, it creates a png file, but a completly empty one.
from kivy.config import Config
# Les configurations doivent être définies avant d'importer d'autres modules Kivy
Config.set('graphics', 'resizable', False)
Config.set('graphics', 'fullscreen', 'auto')
Config.set('kivy', 'exit_on_escape', '0')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Color, Ellipse, Line, Rectangle, Fbo, ClearColor, ClearBuffers
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.colorpicker import ColorPicker
from kivy.uix.slider import Slider
from kivy.uix.filechooser import FileChooserListView
from kivy.uix.textinput import TextInput
from kivy.uix.image import Image
class WidgetPaint(Widget):
def __init__(self, **kwargs):
super(WidgetPaint, self).__init__(**kwargs)
self.largeur_ligne = 15
self.largeur_point = 30
self.dessin = False
self.couleur = (1, 1, 1, 1)
def definir_largeur_ligne(self, largeur):
self.largeur_ligne = largeur
def definir_largeur_point(self, largeur):
self.largeur_point = largeur
def on_touch_down(self, touch):
self.dessin = True
with self.canvas:
Color(*self.couleur)
Ellipse(pos=(touch.x - self.largeur_point / 2, touch.y - self.largeur_point / 2), size=(self.largeur_point, self.largeur_point))
touch.ud['ligne'] = Line(points=(touch.x, touch.y), width=self.largeur_ligne)
def definir_couleur(self, couleur):
self.couleur = couleur
def on_touch_move(self, touch):
ligne = touch.ud.get('ligne')
if ligne:
ligne.points += [touch.x, touch.y]
def effacer_toile(self):
self.canvas.clear()
self.dessin = False
class AppPaint(App):
def build(self):
parent = Widget()
self.peintre = WidgetPaint()
slider = Slider(min=1, max=70, value=15, size_hint=(None, None), size=(200, 50), pos=(295, 60))
slider.bind(value=self.adjust_brush_size)
btn_ouvrir = Button(text='Ouvrir', size_hint=(None, None), size=(100, 50), pos=(150, 285))
btn_ouvrir.bind(on_release=lambda instance: self.ouvrir_fichier())
btn_effacer = Button(text='Effacer', size_hint=(None, None), size=(100, 50), pos=(40, 585))
btn_effacer.bind(on_release=self.effacer_toile)
btn_ligne_fine = Button(text='T1', size_hint=(None, None), size=(50, 50), pos=(105, 60))
btn_ligne_fine.bind(on_release=lambda btn: self.configurer_peintre(5, 10))
btn_ligne_epaisse = Button(text='T2', size_hint=(None, None), size=(50, 50), pos=(155, 60))
btn_ligne_epaisse.bind(on_release=lambda btn: self.configurer_peintre(15, 30))
btn_ligne_plus_epaisse = Button(text='T3', size_hint=(None, None), size=(50, 50), pos=(205, 60))
btn_ligne_plus_epaisse.bind(on_release=lambda btn: self.configurer_peintre(25, 50))
btn_ligne_encore_plus_epaisse = Button(text='T4', size_hint=(None, None), size=(50, 50), pos=(255, 60))
btn_ligne_encore_plus_epaisse.bind(on_release=lambda btn: self.configurer_peintre(35, 70))
btn_quitter = Button(text='Enregistrer & Quitter', size_hint=(None, None), size=(100, 50), pos=(40, 285))
btn_quitter.bind(on_release=lambda instance: self.enregistrer())
btn_rouge = Button(size_hint=(None, None), size=(50, 50), pos=(5, 5), background_color=(5, 0, 0, 1))
btn_rouge.bind(on_release=lambda btn: self.peintre.definir_couleur((5, 0, 0, 1)))
btn_vert = Button(size_hint=(None, None), size=(50, 50), pos=(55, 5), background_color=(0, 5, 0, 1))
btn_vert.bind(on_release=lambda btn: self.peintre.definir_couleur((0, 5, 0, 1)))
btn_bleu = Button(size_hint=(None, None), size=(50, 50), pos=(105, 5), background_color=(0, 0, 5, 1))
btn_bleu.bind(on_release=lambda btn: self.peintre.definir_couleur((0, 0, 5, 1)))
btn_jaune = Button(size_hint=(None, None), size=(50, 50), pos=(155, 5), background_color=(5, 5, 0, 1))
btn_jaune.bind(on_release=lambda btn: self.peintre.definir_couleur((5, 5, 0, 1)))
btn_turquoise = Button(size_hint=(None, None), size=(50, 50), pos=(205, 5), background_color=(0, 5, 5, 1))
btn_turquoise.bind(on_release=lambda btn: self.peintre.definir_couleur((0, 5, 5, 1)))
btn_rose = Button(size_hint=(None, None), size=(50, 50), pos=(255, 5), background_color=(5, 0, 5, 1))
btn_rose.bind(on_release=lambda btn: self.peintre.definir_couleur((5, 0, 5, 1)))
btn_blanc = Button(size_hint=(None, None), size=(50, 50), pos=(305, 5), background_color=(5, 5, 5, 1))
btn_blanc.bind(on_release=lambda btn: self.peintre.definir_couleur((5, 5, 5, 1)))
self.colorpicker_btn = Button(text='Couleurs', size_hint=(None, None), size=(100, 50), pos=(5, 60))
self.colorpicker_btn.bind(on_press=self.roue_de_couleurs)
# Create color boxes
self.saved_colors = [Button(size_hint=(None, None), size=(50, 50), pos=(355 + 50 * i, 5)) for i in range(31)]
for btn in self.saved_colors:
btn.bind(on_release=lambda btn: self.peintre.definir_couleur(btn.background_color))
btn_exporter = Button(text='Exporter', size_hint=(None, None), size=(100, 50), pos=(40, 385))
btn_exporter.bind(on_release=lambda instance: self.export_to_png('dessin.png'))
parent.add_widget(self.peintre)
parent.add_widget(btn_effacer)
parent.add_widget(btn_ligne_fine)
parent.add_widget(btn_ligne_epaisse)
parent.add_widget(btn_ligne_plus_epaisse)
parent.add_widget(btn_ligne_encore_plus_epaisse)
parent.add_widget(btn_quitter)
parent.add_widget(btn_rouge)
parent.add_widget(btn_vert)
parent.add_widget(btn_bleu)
parent.add_widget(btn_jaune)
parent.add_widget(btn_turquoise)
parent.add_widget(btn_rose)
parent.add_widget(self.colorpicker_btn)
parent.add_widget(btn_blanc)
parent.add_widget(btn_ouvrir)
for btn in self.saved_colors:
parent.add_widget(btn)
parent.add_widget(slider)
parent.add_widget(btn_exporter)
self.file_chooser = FileChooserListView()
self.file_chooser.bind(on_submit=self.ouvrir_fichier_selectionné)
self.file_popup = Popup(title='Choisir un fichier', content=self.file_chooser, size_hint=(None, None), size=(400, 400))
return parent
def adjust_brush_size(self, instance, value):
# Met à jour la taille de la ligne et du point basée sur la valeur du slider
self.peintre.definir_largeur_ligne(value)
self.peintre.definir_largeur_point(value * 2) # Par exemple, le point est toujours le double de la largeur de la ligne
def roue_de_couleurs(self, instance):
self.color_picker = ColorPicker()
self.color_picker.bind(color=self.def_couleur)
popup = Popup(title='Sélectionnez une couleur', content=self.color_picker, size_hint=(None, None), size=(400, 400))
popup.open()
popup.bind(on_dismiss=self.save_color_to_box)
def def_couleur(self, instance, color):
self.peintre.definir_couleur(color)
def ajuster_couleur(self, couleur):
facteur = 0.2
moyenne = sum(couleur[:3]) / 3
nouvelle_couleur = [(c + facteur * (c - moyenne)) for c in couleur[:3]]
nouvelle_couleur = [min(max(0, c), 1) for c in nouvelle_couleur]
return tuple(nouvelle_couleur + [couleur[3]])
def save_color_to_box(self, instance):
couleur = self.color_picker.color
satcoul = self.ajuster_couleur(couleur)
couleur_assignée = False
for btn in self.saved_colors:
if btn.background_color == [1, 1, 1, 1] and not couleur_assignée:
btn.background_color = satcoul
couleur_assignée = True
def configurer_peintre(self, largeur_ligne, largeur_point):
self.peintre.definir_largeur_ligne(largeur_ligne)
self.peintre.definir_largeur_point(largeur_point)
def effacer_toile(self, obj):
self.peintre.effacer_toile()
print('Effacé !')
def ouvrir_fichier(self):
# Afficher le FileChooser
self.file_popup.open()
def ouvrir_fichier_selectionné(self, instance, selection, _):
# Fermer le FileChooser
self.file_popup.dismiss()
# Vérifier si un fichier a été sélectionné
if selection:
chemin_fichier = selection[0]
self.peintre.effacer_toile() # Effacer le dessin actuel
with self.peintre.canvas:
Rectangle(source=chemin_fichier, pos=self.peintre.pos, size=self.peintre.size)
print(f'Fichier ouvert: {chemin_fichier}')
def export_to_png(self, filename):
'''Exporte le dessin actuel dans un fichier PNG.'''
# Créer un objet Fbo avec la même taille que le widget de peinture
fbo = Fbo(size=self.peintre.size)
# Ajouter le widget de peinture à l'objet Fbo
with fbo:
# Effacer l'objet Fbo avec une couleur transparente
ClearColor(0, 0, 0, 0)
ClearBuffers()
# Dessiner le contenu du widget de peinture
self.peintre.canvas.draw()
# Enregistrer la texture de l'objet Fbo dans un fichier PNG
fbo.texture.save(filename)
print(f'Dessin sauvegardé sous {filename} !')
def enregistrer(self):
if self.peintre.dessin:
content = BoxLayout(orientation='vertical')
msg = Label(text='Vous avez des changements non enregistrés. Voulez-vous enregistrer ?')
btn_layout = BoxLayout(size_hint_y=None, height='70', orientation='horizontal')
oui_btn = Button(text='Oui', on_release=lambda *args: self.sauvegarder_toile())
non_btn = Button(text='Non', on_release=lambda *args: App().stop())
btn_layout.add_widget(oui_btn)
btn_layout.add_widget(non_btn)
content.add_widget(msg)
content.add_widget(btn_layout)
self.save_popup = Popup(title='Confirmation de Sauvegarde', content=content, size_hint=(None, None), size=(400, 200), auto_dismiss=False)
self.save_popup.open()
else:
print('Rien à enregistrer')
self.stop()
def sauvegarder_toile(self):
if self.peintre.dessin:
# Créer une boîte de dialogue d'entrée textuelle pour saisir le nouveau nom du fichier
content = BoxLayout(orientation='vertical')
msg = Label(text='Saisissez le nouveau nom du fichier (sans extension) :')
input_filename = TextInput(hint_text='Nom du fichier')
btn_layout = BoxLayout(size_hint_y=None, height='70', orientation='horizontal')
sauvegarder_btn = Button(text='Sauvegarder', on_release=lambda *args: self.sauvegarder_avec_nom(input_filename.text))
annuler_btn = Button(text='Annuler', on_release=lambda *args: self.save_popup.dismiss())
btn_layout.add_widget(sauvegarder_btn)
btn_layout.add_widget(annuler_btn)
content.add_widget(msg)
content.add_widget(input_filename)
content.add_widget(btn_layout)
self.save_popup = Popup(title='Sauvegarder le dessin', content=content, size_hint=(None, None), size=(500, 200), auto_dismiss=False)
self.save_popup.open()
else:
print('Rien à enregistrer')
def sauvegarder_avec_nom(self, filename):
if filename:
if not filename.endswith('.png'):
filename += '.png' # Ajouter l'extension .png si elle n'est pas déjà présente
self.export_to_png(filename)
print(f'Dessin sauvegardé sous {filename} !')
else:
print('Nom de fichier invalide.')
self.save_popup.dismiss()
AppPaint().stop()
def profil(self):
pass
if __name__ == '__main__':
AppPaint().run()
Usually i use AI to try to help me but they didn't work, same for kivy documentation, maybe i'm not using the right modules idk. Export just make my app close
At some trials here a solutions which you may save it without problem:
#
from kivy.core.window import Window
def export_to_png(self, filename):
print(f"Exporting to {filename}...")
try:
print('Size', self.peintre.size, 'Filename: ', filename)
self.peintre.size = (Window.size[0], Window.size[1])
self.peintre.export_to_png(filename)
except Exception as e:
print(f"Error while exporting: {e}")
raise e
from my test, its works without problem. However, If you need, I will share full code as you shared.
EDIT: Here full code:
from kivy.config import Config
from kivy.core.window import Window
# Les configurations doivent être définies avant d'importer d'autres modules Kivy
Config.set('graphics', 'resizable', False)
Config.set('graphics', 'fullscreen', 'auto')
Config.set('kivy', 'exit_on_escape', '0')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Color, Ellipse, Line, Rectangle, Fbo, ClearColor, ClearBuffers
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.colorpicker import ColorPicker
from kivy.uix.slider import Slider
from kivy.uix.filechooser import FileChooserListView
from kivy.uix.textinput import TextInput
from kivy.uix.image import Image
class WidgetPaint(Widget):
def __init__(self, **kwargs):
super(WidgetPaint, self).__init__(**kwargs)
self.largeur_ligne = 15
self.largeur_point = 30
self.dessin = False
self.couleur = (1, 1, 1, 1)
def definir_largeur_ligne(self, largeur):
self.largeur_ligne = largeur
def definir_largeur_point(self, largeur):
self.largeur_point = largeur
def on_touch_down(self, touch):
self.dessin = True
with self.canvas:
Color(*self.couleur)
Ellipse(pos=(touch.x - self.largeur_point / 2, touch.y - self.largeur_point / 2), size=(self.largeur_point, self.largeur_point))
touch.ud['ligne'] = Line(points=(touch.x, touch.y), width=self.largeur_ligne)
def definir_couleur(self, couleur):
self.couleur = couleur
def on_touch_move(self, touch):
ligne = touch.ud.get('ligne')
if ligne:
ligne.points += [touch.x, touch.y]
def effacer_toile(self):
self.canvas.clear()
self.dessin = False
class AppPaint(App):
def build(self):
parent = Widget()
self.peintre = WidgetPaint()
slider = Slider(min=1, max=70, value=15, size_hint=(None, None), size=(200, 50), pos=(295, 60))
slider.bind(value=self.adjust_brush_size)
btn_ouvrir = Button(text='Ouvrir', size_hint=(None, None), size=(100, 50), pos=(150, 285))
btn_ouvrir.bind(on_release=lambda instance: self.ouvrir_fichier())
btn_effacer = Button(text='Effacer', size_hint=(None, None), size=(100, 50), pos=(40, 585))
btn_effacer.bind(on_release=self.effacer_toile)
btn_ligne_fine = Button(text='T1', size_hint=(None, None), size=(50, 50), pos=(105, 60))
btn_ligne_fine.bind(on_release=lambda btn: self.configurer_peintre(5, 10))
btn_ligne_epaisse = Button(text='T2', size_hint=(None, None), size=(50, 50), pos=(155, 60))
btn_ligne_epaisse.bind(on_release=lambda btn: self.configurer_peintre(15, 30))
btn_ligne_plus_epaisse = Button(text='T3', size_hint=(None, None), size=(50, 50), pos=(205, 60))
btn_ligne_plus_epaisse.bind(on_release=lambda btn: self.configurer_peintre(25, 50))
btn_ligne_encore_plus_epaisse = Button(text='T4', size_hint=(None, None), size=(50, 50), pos=(255, 60))
btn_ligne_encore_plus_epaisse.bind(on_release=lambda btn: self.configurer_peintre(35, 70))
btn_quitter = Button(text='Enregistrer & Quitter', size_hint=(None, None), size=(100, 50), pos=(40, 285))
btn_quitter.bind(on_release=lambda instance: self.enregistrer())
btn_rouge = Button(size_hint=(None, None), size=(50, 50), pos=(5, 5), background_color=(5, 0, 0, 1))
btn_rouge.bind(on_release=lambda btn: self.peintre.definir_couleur((5, 0, 0, 1)))
btn_vert = Button(size_hint=(None, None), size=(50, 50), pos=(55, 5), background_color=(0, 5, 0, 1))
btn_vert.bind(on_release=lambda btn: self.peintre.definir_couleur((0, 5, 0, 1)))
btn_bleu = Button(size_hint=(None, None), size=(50, 50), pos=(105, 5), background_color=(0, 0, 5, 1))
btn_bleu.bind(on_release=lambda btn: self.peintre.definir_couleur((0, 0, 5, 1)))
btn_jaune = Button(size_hint=(None, None), size=(50, 50), pos=(155, 5), background_color=(5, 5, 0, 1))
btn_jaune.bind(on_release=lambda btn: self.peintre.definir_couleur((5, 5, 0, 1)))
btn_turquoise = Button(size_hint=(None, None), size=(50, 50), pos=(205, 5), background_color=(0, 5, 5, 1))
btn_turquoise.bind(on_release=lambda btn: self.peintre.definir_couleur((0, 5, 5, 1)))
btn_rose = Button(size_hint=(None, None), size=(50, 50), pos=(255, 5), background_color=(5, 0, 5, 1))
btn_rose.bind(on_release=lambda btn: self.peintre.definir_couleur((5, 0, 5, 1)))
btn_blanc = Button(size_hint=(None, None), size=(50, 50), pos=(305, 5), background_color=(5, 5, 5, 1))
btn_blanc.bind(on_release=lambda btn: self.peintre.definir_couleur((5, 5, 5, 1)))
self.colorpicker_btn = Button(text='Couleurs', size_hint=(None, None), size=(100, 50), pos=(5, 60))
self.colorpicker_btn.bind(on_press=self.roue_de_couleurs)
# Create color boxes
self.saved_colors = [Button(size_hint=(None, None), size=(50, 50), pos=(355 + 50 * i, 5)) for i in range(31)]
for btn in self.saved_colors:
btn.bind(on_release=lambda btn: self.peintre.definir_couleur(btn.background_color))
btn_exporter = Button(text='Exporter', size_hint=(None, None), size=(100, 50), pos=(40, 385))
btn_exporter.bind(on_release=lambda instance: self.export_to_png('dessin.png'))
parent.add_widget(self.peintre)
parent.add_widget(btn_effacer)
parent.add_widget(btn_ligne_fine)
parent.add_widget(btn_ligne_epaisse)
parent.add_widget(btn_ligne_plus_epaisse)
parent.add_widget(btn_ligne_encore_plus_epaisse)
parent.add_widget(btn_quitter)
parent.add_widget(btn_rouge)
parent.add_widget(btn_vert)
parent.add_widget(btn_bleu)
parent.add_widget(btn_jaune)
parent.add_widget(btn_turquoise)
parent.add_widget(btn_rose)
parent.add_widget(self.colorpicker_btn)
parent.add_widget(btn_blanc)
parent.add_widget(btn_ouvrir)
for btn in self.saved_colors:
parent.add_widget(btn)
parent.add_widget(slider)
parent.add_widget(btn_exporter)
self.file_chooser = FileChooserListView()
self.file_chooser.bind(on_submit=self.ouvrir_fichier_selectionné)
self.file_popup = Popup(title='Choisir un fichier', content=self.file_chooser, size_hint=(None, None), size=(400, 400))
return parent
def adjust_brush_size(self, instance, value):
# Met à jour la taille de la ligne et du point basée sur la valeur du slider
self.peintre.definir_largeur_ligne(value)
self.peintre.definir_largeur_point(value * 2) # Par exemple, le point est toujours le double de la largeur de la ligne
def roue_de_couleurs(self, instance):
self.color_picker = ColorPicker()
self.color_picker.bind(color=self.def_couleur)
popup = Popup(title='Sélectionnez une couleur', content=self.color_picker, size_hint=(None, None), size=(400, 400))
popup.open()
popup.bind(on_dismiss=self.save_color_to_box)
def def_couleur(self, instance, color):
self.peintre.definir_couleur(color)
def ajuster_couleur(self, couleur):
facteur = 0.2
moyenne = sum(couleur[:3]) / 3
nouvelle_couleur = [(c + facteur * (c - moyenne)) for c in couleur[:3]]
nouvelle_couleur = [min(max(0, c), 1) for c in nouvelle_couleur]
return tuple(nouvelle_couleur + [couleur[3]])
def save_color_to_box(self, instance):
couleur = self.color_picker.color
satcoul = self.ajuster_couleur(couleur)
couleur_assignée = False
for btn in self.saved_colors:
if btn.background_color == [1, 1, 1, 1] and not couleur_assignée:
btn.background_color = satcoul
couleur_assignée = True
def configurer_peintre(self, largeur_ligne, largeur_point):
self.peintre.definir_largeur_ligne(largeur_ligne)
self.peintre.definir_largeur_point(largeur_point)
def effacer_toile(self, obj):
self.peintre.effacer_toile()
print('Effacé !')
def ouvrir_fichier(self):
# Afficher le FileChooser
self.file_popup.open()
def ouvrir_fichier_selectionné(self, instance, selection, _):
# Fermer le FileChooser
self.file_popup.dismiss()
# Vérifier si un fichier a été sélectionné
if selection:
chemin_fichier = selection[0]
self.peintre.effacer_toile() # Effacer le dessin actuel
with self.peintre.canvas:
Rectangle(source=chemin_fichier, pos=self.peintre.pos, size=self.peintre.size)
print(f'Fichier ouvert: {chemin_fichier}')
def export_to_png(self, filename):
print(f"Exporting to {filename}...")
try:
print('Size', self.peintre.size, 'Filename: ', filename)
self.peintre.size = (Window.size[0], Window.size[1])
self.peintre.export_to_png(filename)
except Exception as e:
print(f"Error while exporting: {e}")
raise e
def enregistrer(self):
if self.peintre.dessin:
content = BoxLayout(orientation='vertical')
msg = Label(text='Vous avez des changements non enregistrés. Voulez-vous enregistrer ?')
btn_layout = BoxLayout(size_hint_y=None, height='70', orientation='horizontal')
oui_btn = Button(text='Oui', on_release=lambda *args: self.sauvegarder_toile())
non_btn = Button(text='Non', on_release=lambda *args: App().stop())
btn_layout.add_widget(oui_btn)
btn_layout.add_widget(non_btn)
content.add_widget(msg)
content.add_widget(btn_layout)
self.save_popup = Popup(title='Confirmation de Sauvegarde', content=content, size_hint=(None, None), size=(400, 200), auto_dismiss=False)
self.save_popup.open()
else:
print('Rien à enregistrer')
self.stop()
def sauvegarder_toile(self):
if self.peintre.dessin:
# Créer une boîte de dialogue d'entrée textuelle pour saisir le nouveau nom du fichier
content = BoxLayout(orientation='vertical')
msg = Label(text='Saisissez le nouveau nom du fichier (sans extension) :')
input_filename = TextInput(hint_text='Nom du fichier')
btn_layout = BoxLayout(size_hint_y=None, height='70', orientation='horizontal')
sauvegarder_btn = Button(text='Sauvegarder', on_release=lambda *args: self.sauvegarder_avec_nom(input_filename.text))
annuler_btn = Button(text='Annuler', on_release=lambda *args: self.save_popup.dismiss())
btn_layout.add_widget(sauvegarder_btn)
btn_layout.add_widget(annuler_btn)
content.add_widget(msg)
content.add_widget(input_filename)
content.add_widget(btn_layout)
self.save_popup = Popup(title='Sauvegarder le dessin', content=content, size_hint=(None, None), size=(500, 200), auto_dismiss=False)
self.save_popup.open()
else:
print('Rien à enregistrer')
def sauvegarder_avec_nom(self, filename):
if filename:
if not filename.endswith('.png'):
filename += '.png' # Ajouter l'extension .png si elle n'est pas déjà présente
self.export_to_png(filename)
print(f'Dessin sauvegardé sous {filename} !')
else:
print('Nom de fichier invalide.')
self.save_popup.dismiss()
AppPaint().stop()
def profil(self):
pass
if __name__ == '__main__':
AppPaint().run()
WidgetPaint
class has inherited functions such as export_to_png
that you have not defined.