[GTK3-Python3]Modélisation de champs maraîchers pour analyse bio-dynamique

Bonjour,

De retour d’aventure, reprise du projet.

De nouvelles facilités pour soliciter l’interface grafique avec GTK3,
Mais :thinking:, de précieux avis seraient idéaux.
À tout hazard, :blush: ici peut-être.
Bref →

Une modélisation avec Window-Box-Frame-Paned :

#!/usr/bin/env python3

import gi

gi.require_version('Gtk', "3.0")
from gi.repository import Gtk

class champ(Gtk.Window) :
	def __init__(self) :

		Gtk.Window.__init__(self) #Initialisation de la fenetre principale
		self.set_title("Cultures")
		self.set_default_size(500,700)

		Champ0 = Gtk.Box(spacing=6) #Initialisation de la première Box
		Champ0.set_orientation(Gtk.Orientation.VERTICAL)
		self.add(Champ0)

		global parcelles_champ #Superposition des Box
		parcelles_champ = []
		for i in range(0, 3) : #Quantité de parcelle x 2
			parcelles_champ.append(f"Parcelle{i}_{i}")
			parcelles_champ[i] = Gtk.Box(spacing=6)
			parcelles_champ[i].set_orientation(Gtk.Orientation.HORIZONTAL)
			Champ0.pack_start(parcelles_champ[i], True, True, 0)


		self_frame = [] #Enchainement des Frames
		frame = []
		for i in range (0, 6) : #Initialisation des Frames
			self_frame.append(f"self.frame{i}")
			frame.append(f"Frame{i}")
			self_frame[i] = Gtk.Frame(label=frame[i]) 	
					
		for i, j in enumerate(self_frame) : #3 x 2 parcelles
			if i <= 1 :
				planches(0, j)
			elif i <= 3 :
				planches(1, j)
			else :
				planches(2, j)


def planches(n, parcelle) :

	parcelles_champ[n].pack_start(parcelle, True, True, 0)

	hpaned0 = Gtk.Paned(orientation = Gtk.Orientation.VERTICAL)
	hpaned0.set_position(15)
	parcelle.add(hpaned0)
	label = Gtk.Label(label="Left Panel")
	hpaned0.add(label)

	vpaned0 = Gtk.Paned(orientation = Gtk.Orientation.HORIZONTAL)
	vpaned0.set_position(15)
	hpaned0.pack2(vpaned0, True, True)
	label = Gtk.Label(label="Top Panel")
	vpaned0.add1(label)

	vpaned1 = Gtk.Paned(orientation = Gtk.Orientation.VERTICAL)
	vpaned1.set_position(15)
	vpaned0.pack2(vpaned1, True, True)
	label = Gtk.Label(label="Hariots courants, patates enterrés \n ou tout autres formes de vitamines et oligaux éléments à dévorer")
	vpaned1.add2(label)
	
	vpaned2 = Gtk.Paned(orientation = Gtk.Orientation.HORIZONTAL)
	vpaned2.set_position(15)
	vpaned1.add(vpaned2)
	label = Gtk.Label(label="Plussss")
	vpaned2.add1(label)
	
	vpaned3 = Gtk.Paned(orientation = Gtk.Orientation.HORIZONTAL)
	vpaned3.set_position(15)
	vpaned2.add(vpaned3)
	label = Gtk.Label(label="Moinsssssssssssss")
	vpaned3.add1(label)


disp = champ()
disp.connect("destroy", Gtk.main_quit)
disp.show_all()
Gtk.main()

Mais Gtk_Paned ne se divise que par deux, alors que j’aurais besoin de division par huit.

Et l’option Window-Box-Grid-Frame

#!/usr/bin/env python3

import gi

gi.require_version('Gtk', "3.0")
from gi.repository import Gtk

class champ(Gtk.Window):
	def __init__(self):

		Gtk.Window.__init__(self)
		self.set_title("Cultures")
		self.set_default_size(500,700)

		Champ0 = Gtk.Box(spacing=6)
		Champ0.set_orientation(Gtk.Orientation.VERTICAL)
		self.add(Champ0)


		global parcelles_champ #Superposition des Box
		parcelles_champ = []
		for i in range(0, 3) : #Quantité de parcelle x 2
			parcelles_champ.append(f"Parcelle{i}_{i}")
			parcelles_champ[i] = Gtk.Box(spacing=6)
			parcelles_champ[i].set_orientation(Gtk.Orientation.HORIZONTAL)
			Champ0.pack_start(parcelles_champ[i], False, True, 0)


		global parcelles_planches
		parcelles_planches = []
		for i in range(0, 3) :	
			parcelles_planches.append(f"Parcelle{i}_{0}_Planches")
			parcelles_planches.append(f"Parcelle{i}_{1}_Planches")




		for i in range(0, 6) :
			parcelles_planches[i] = Gtk.Grid()
			parcelles_planches[i].set_row_spacing(5)
			parcelles_planches[i].set_column_spacing(5)
			if i <= 1 :
				parcelles_champ[0].add(parcelles_planches[i])
			elif i <= 3 :
				parcelles_champ[1].add(parcelles_planches[i])
			else :
				parcelles_champ[2].add(parcelles_planches[i])
			planches(parcelles_planches[i])


def planches(P_Planches):
	Frame00 = Gtk.Frame(label="Frame00")
	Frame00.set_label_align(0, 0)
	P_Planches.attach(Frame00, 0, 0, 21, 1)

	Frame01 = Gtk.Frame()
	Frame01.set_label("Frame01")
	Frame01.set_label_align(0, 0)
	P_Planches.attach(Frame01, 0, 1, 1, 17)

	Frame02 = Gtk.Frame(label="Frame02")
	Frame02.set_label_align(0, 0)
	P_Planches.attach(Frame02, 0, 20, 21, 1)

	Frame03 = Gtk.Frame(label="Frame03")
	P_Planches.attach(Frame03, 20, 1, 1, 17)

	Frame00 = Gtk.Frame(label="Frame00")
	Frame00.set_label_align(0, 0)
	P_Planches.attach(Frame00, 1, 1, 19, 2)

	Frame01 = Gtk.Frame(label="Frame01")
	Frame01.set_label_align(0, 0)
	P_Planches.attach(Frame01, 1, 3, 19, 2)

	Frame02 = Gtk.Frame(label="Frame02")
	Frame02.set_label_align(0, 0)
	P_Planches.attach(Frame02, 1, 5, 19, 2)

	Frame03 = Gtk.Frame(label="Frame03")
	Frame03.set_label_align(0, 0)
	P_Planches.attach(Frame03, 1, 7, 19, 2)

	Frame04 = Gtk.Frame(label="Frame04")
	Frame04.set_label_align(0, 0)
	P_Planches.attach(Frame04, 1, 9, 19, 2)

	Frame05 = Gtk.Frame(label="Frame05")
	Frame05.set_label_align(0, 0)
	P_Planches.attach(Frame05, 1, 11, 19, 2)

	Frame06 = Gtk.Frame(label="Frame06")
	Frame06.set_label_align(0, 0)
	P_Planches.attach(Frame06, 1, 13, 19, 2)

	Frame07 = Gtk.Frame(label="Frame07")
	Frame07.set_label_align(0, 0)
	P_Planches.attach(Frame07, 1, 15, 19, 2)




disp = champ()
disp.connect("destroy", Gtk.main_quit)
disp.show_all()
Gtk.main()

Mais contraint par les dimensions invariables des lignes et des colonnes, le redimensionnement de la fenêtre, entre écran et contenu est problématique.

Au cas où l’évidence m’échappe,
Je tenais à partager.

Merci pour votre attention

Bonjour,

Je ne sais pas si je comprends très bien ce que tu veux, du coup, je vais te demander au cas où.
Ce que tu veux, c’est bien avoir un truc comme le second code, mais qui soit redimensionnable comme le premier code ?

Bonjour Almtesh,

Je cherche comment modéliser
./ des planches potagères (ayant éventuellement des configurations multiples : rectangle avec bordure, sans bordure, planches rondes … selon la géométrie de la culture existante)
./ sur des parcelles,
./ dans des champs.

L’idée serait de pouvoir afficher l’état présent d’une culture (information concernant la planche et sa culture). Une autre partie du programme devrait proposer une analyse biodynamique sur un historique d’au moins 5 ans)

Un autre exemple qu’avec des Gtk.Box() :
#!/usr/bin/env python3

import gi

gi.require_version('Gtk', "3.0")
from gi.repository import Gtk, Gdk

class champ(Gtk.Window) :
	def __init__(self) :

		Gtk.Window.__init__(self) #Initialisation de la fenetre principale
		self.set_title("Cultures")

		parcelle_carré(self)


def parcelle_carré(self):
	Champ0 = Gtk.Box(spacing=0) #Conteneur
	Champ0.set_orientation(Gtk.Orientation.VERTICAL)
	self.add(Champ0)


# BordureNord Centre BordureSud
	Champ01 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
	Champ0.pack_start(Champ01, False, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ01.add(button)

	Champ02 = Gtk.Box(spacing=0)
	Champ02.set_orientation(Gtk.Orientation.HORIZONTAL)
	Champ0.pack_start(Champ02, True, False, 0)

	Champ03 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
	Champ0.pack_start(Champ03, False, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ03.add(button)


# BordureOuest Centre BordureEst
	Champ002 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
	Champ02.add(Champ002)
	label = Gtk.Label()
	label.set_text("Click me")
	label.set_angle(90)
	button = Gtk.Button()
	button.add(label)
	Champ002.pack_start(button, True, True, 0)

	Champ003 = Gtk.Box(spacing=0)
	Champ003.set_orientation(Gtk.Orientation.VERTICAL)
	Champ02.pack_start(Champ003, False, False, 2)

	Champ004 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
	Champ02.add(Champ004)
	label = Gtk.Label()
	label.set_text("Click me")
	label.set_angle(90)
	button = Gtk.Button()
	button.add(label)
	Champ004.pack_start(button, True, True, 0)


# Association de culture x2
	Champ0003 = Gtk.Box(spacing=2)
	Champ0003.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0003, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0003.add(button)

	Champ0004 = Gtk.Box(spacing=2)
	Champ0004.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0004, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0004.add(button)

	Champ0005 = Gtk.Box(spacing=2)
	Champ0005.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0005, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0005.add(button)

	Champ0006 = Gtk.Box(spacing=2)
	Champ0006.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0006, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0006.add(button)

	Champ0007 = Gtk.Box(spacing=2)
	Champ0007.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0007, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0007.add(button)

	Champ0008 = Gtk.Box(spacing=2)
	Champ0008.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0008, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0008.add(button)

	Champ0009 = Gtk.Box(spacing=2)
	Champ0009.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0009, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0009.add(button)

	Champ00010 = Gtk.Box(spacing=2)
	Champ00010.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ00010, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ00010.add(button)

	Area0 = Gtk.DrawingArea()
	Area0.set_size_request(250, 0)
	Champ003.add(Area0)


# Label à ajouter, correspondance de la culture de la planche à préciser celon la nature de l'historique.



disp = champ()
disp.connect("destroy", Gtk.main_quit)
disp.show_all()
Gtk.main()

Après des essais avec glade. En remplaçant, les couleurs de fond par des boutons, la solution a l’air viable. Je dois encore vérifier le comportement des barres de défilement et le redimensionnement de fenêtre pour avoir tout le champ avec toutes ses parcelles dans une fenêtre aux dimensions de l’écran.

L’idée est là, je cherche la meilleure technique pour la développer.
Ce serait éventuellement de reprendre la structure des SADT.
Une fenêtre par système.
Mais j’ai l’impression de travailler en .PNG ou en .jpg alors que j’aimerais faire du .svg.
Je dois avoir les cases aux mauvaises endroits :crazy_face:.

Merci encore pour votre attention,

salut
mon expérience de ce genre de programmes est que les objets graphiques ne doivent pas être intégrés dans d’autres objets graphiques.
Tu as ta fenetre principale avec un bloc principal, et dedans chaque objet est indépendant des autres. LA dépendance est gérée par une classe logique externe non graphique . Sinon tu auras toujorus des problèmes liés par exemple aux parcelles qui se chevauchent ( par ex 1.parcelle de poireaux 2. parcelle de pomme de terre qui chevauche 3. parcelle derrière une butte 4. parcelle à côté du ru … ) car n’ayant pas la amême fonction; encore plus en permaculture, comme dirait mon pote gab.

Salut dindoun,

Je dois encore comprendre comment fonctionne les conteneurs,

La prochaine étape serait d’ouvrir les parcelles du champ dans des onglets de type Gtk.Notebook() du genre :

#!/usr/bin/env python3

import gi

gi.require_version('Gtk', "3.0")
from gi.repository import Gtk

class champ(Gtk.Window) :
	def __init__(self) :

		Gtk.Window.__init__(self) #Initialisation de la fenetre principale
		self.set_title("Cultures")

		self.notebook = Gtk.Notebook()
		self.add(self.notebook)





		Champ0 = Gtk.Box(spacing=6) #Initialisation de la première Box
		Champ0.set_orientation(Gtk.Orientation.VERTICAL)
		self.add(Champ0)

		global parcelles_champ #Superposition des Box
		parcelles_champ = []
		for i in range(0, 3) : #Quantité de parcelle x 2
			parcelles_champ.append(f"Parcelle{i}_{i}")
			parcelles_champ[i] = Gtk.Box(spacing=6)
			parcelles_champ[i].set_orientation(Gtk.Orientation.HORIZONTAL)
			Champ0.pack_start(parcelles_champ[i], True, True, 0)

		self_box = [] #Enchainement des boxs
		box = []
		for i in range (0, 6) : #Initialisation des boxs
			self_box.append(f"self.box{i}")
			box.append(f"Champ{i}")
			self_box[i] = Gtk.Box()
			label = Gtk.Label()
			label.set_text(box[i])
			button = Gtk.Button()
			button.add(label)
			button.connect("clicked", self.on_open_clicked)
			self_box[i].pack_start(button, True, True, 0)
					
		for i, j in enumerate(self_box) : #3 x 2 parcelles
			if i <= 1 :
				parcelles_champ[0].pack_start(self_box[i], True, True, 0)
			elif i <= 3 :
				parcelles_champ[1].pack_start(self_box[i], True, True, 0)
			else :
				parcelles_champ[2].pack_start(self_box[i], True, True, 0)
					



		self.page1 = Champ0
		self.notebook.append_page(self.page1, Gtk.Label(label="Champ"))


	def on_open_clicked(self, button):

		Gtk.main_quit()


		disp = champ1()
		disp.connect("destroy", Gtk.main_quit)
		disp.show_all()
		Gtk.main()




class champ1(Gtk.Window) :
	def __init__(self) :

		Gtk.Window.__init__(self) #Initialisation de la fenetre principale
		self.set_title("Cultures")

		self.notebook = Gtk.Notebook()
		self.add(self.notebook)


		parcelle_carré(self)
		self.page2 = Champ00
		self.notebook.append_page(self.page2, Gtk.Label(label="Parcelle"))

def parcelle_carré(self):
	global Champ00
	Champ00 = Gtk.Box(spacing=0) #Conteneur
	Champ00.set_orientation(Gtk.Orientation.VERTICAL)
	self.add(Champ00)


# BordureNord Centre BordureSud
	Champ01 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
	Champ00.pack_start(Champ01, False, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ01.add(button)

	Champ02 = Gtk.Box(spacing=0)
	Champ02.set_orientation(Gtk.Orientation.HORIZONTAL)
	Champ00.pack_start(Champ02, True, False, 0)

	Champ03 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
	Champ00.pack_start(Champ03, False, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ03.add(button)


# BordureOuest Centre BordureEst
	Champ002 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
	Champ02.add(Champ002)
	label = Gtk.Label()
	label.set_text("Click me")
	label.set_angle(90)
	button = Gtk.Button()
	button.add(label)
	Champ002.pack_start(button, True, True, 0)

	Champ003 = Gtk.Box(spacing=0)
	Champ003.set_orientation(Gtk.Orientation.VERTICAL)
	Champ02.pack_start(Champ003, False, False, 2)

	Champ004 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
	Champ02.add(Champ004)
	label = Gtk.Label()
	label.set_text("Click me")
	label.set_angle(90)
	button = Gtk.Button()
	button.add(label)
	Champ004.pack_start(button, True, True, 0)


# Association de culture x2 ou x8
	Champ0003 = Gtk.Box(spacing=2)
	Champ0003.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0003, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0003.add(button)

	Champ0004 = Gtk.Box(spacing=2)
	Champ0004.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0004, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0004.add(button)

	Champ0005 = Gtk.Box(spacing=2)
	Champ0005.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0005, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0005.add(button)

	Champ0006 = Gtk.Box(spacing=2)
	Champ0006.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0006, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0006.add(button)

	Champ0007 = Gtk.Box(spacing=2)
	Champ0007.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0007, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0007.add(button)

	Champ0008 = Gtk.Box(spacing=2)
	Champ0008.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0008, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0008.add(button)

	Champ0009 = Gtk.Box(spacing=2)
	Champ0009.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ0009, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ0009.add(button)

	Champ00010 = Gtk.Box(spacing=2)
	Champ00010.set_orientation(Gtk.Orientation.VERTICAL)
	Champ003.pack_start(Champ00010, True, False, 0)
	button = Gtk.Button.new_with_label("Click Me")
	Champ00010.add(button)

	Area0 = Gtk.DrawingArea()
	Area0.set_size_request(250, 0)
	Champ003.add(Area0)


# Label à ajouter, correspondance de la culture de la planche à préciser celon la nature de l'historique.







disp = champ()
disp.connect("destroy", Gtk.main_quit)
disp.show_all()
Gtk.main()

Et pour ce qui est des classes, je croise les doigts pour éviter l’intégration de CSS.

J’espère me familiariser avec ces classes par l’intermédiaire des décorations de classe de données @dateclass pour la construction des listes et dictionnaires à analyser par la suite

Merci pour ton intervention.

P.S. : Concernant le redimentionnement,
Je comprends mal pourquoi il est possible de redimentionner au miniminimini le champ, mais pas la parcelle.
Une affaire de block enfant dépendant du block principale :thinking:

Bon weekend :slight_smile:

Bonjour,

Bonne année,

Encore quelques bricoles à terminer pour faire interagir les données avec la visualisation, mais l’idée de l’analyse bio-dynamique (sur les seules et simples familles de végétaux) commence à prendre forme:).

Je me demande s’il serait possible de réaliser des boutons GTK de forme circulaire avec Cairo ?

La visualisation ici :

#!/usr/bin/env python3


import gi
gi.require_version('Gtk', "3.0")
from gi.repository import Gtk, Gdk
from enum import *
import time
from dataclasses import dataclass, asdict, astuple

@dataclass(order=True)
class Végétal :
	nom : str
	famille : str
	racine : str
	longévité : str
	début_semi : str
	fin_semi : str
	début_plantation : str
	fin_plantation : str
	consommé : str


Plantes = [	Végétal('Asperge', 'Asparagacé', 'Fasciculé', 'Vivace', 'NA', 'NA', 'Hiver1', 'Hiver3', 'Feuilles'),
	 	Végétal('Artichaut','Astéracé', 'Fasciculé', 'Vivace', 'NA', 'NA', 'NA', 'NA', 'Fleur'),
		Végétal('Pomme de terre', 'Solanacé', 'Bulbe', 'Annuel', 'NA', 'NA', 'Printemps1', 'Printemps2', 'Racine'),
		Végétal('Ail', 'Liliacé', 'Bulbee', 'Annuel', 'NA', 'NA', 'Été3', 'Automne1', 'Racine'),
		Végétal('Oignon', 'Liliacé', 'Bulbe', 'Biannuel', 'Printemps2', 'Printemps3', 'Été3', 'Automne1', 'Racine'),
		Végétal('Échalote', 'Liliacé', 'Bulbe', 'Annuel', 'NA', 'NA', 'Été3', 'Automne1', 'Racine'),
		Végétal('Fève', 'Fabacé', 'Fasciculé', 'Annuel', 'Été3', 'Automne1', 'NA', 'NA', 'Fruit'),
		Végétal('Ciboulette', 'Liliacé', 'Fasciculé', 'Vivace', 'NA', 'NA', 'Automne1', 'Automne2', 'Feuille'),
		Végétal('Coriandre', 'Apiacé', 'Fasciculé', 'Annuel', 'Été3', 'Automne1', 'NA', 'NA', 'Feuille'),
		Végétal('Persil', 'Apiacé', 'Pivot', 'Annuel', 'Printemps2', 'Printemps3', 'NA', 'NA', 'Feuille'),
		Végétal('Pimprenelle', 'Rosacé', 'Fasciculé', 'Vivace', 'Printemps2', 'Printemps3', 'Automne1', 'Automne2', 'Feuille'),
		Végétal('Romarin', 'Lamiacé', 'Fasciculé', 'Vivace', 'NA', 'NA', 'Automne1', 'Automne2', 'Feuille'),
		Végétal('Sauge_officinale', 'Lamiacé', 'Fasciculé', 'Vivace', 'NA', 'NA', 'Automne1', 'Automne2', 'Feuille'),
		Végétal('Sauge_sclarée', 'Lamiacé', 'Fasciculé', 'Bisannuel', 'Printemps2', 'Printemps3', 'NA', 'NA', 'Fleur'),
		Végétal('Lavande', 'Lamiacé', 'Fasciculé', 'Vivace', 'NA', 'NA', 'Automne1', 'Automne2', 'Fleur'),
		Végétal('Courgette', 'Cucurbitacé', 'Fasciculé', 'Annuel', 'Printemps2', 'Printemps3', 'Printemps3', 'Été1', 'Fruit'),
		Végétal('Navet', 'Brassicacé', 'Pivot', 'Annuel', 'Été3', 'Automne1', 'NA', 'NA', 'Racine'),
		Végétal('Betterave', 'Chénopodiacées', 'Pivot', 'Annuel', 'Été3', 'Automne1', 'NA', 'NA', 'Racine')]



@dataclass(order=True)
class Parcelle:
	nom : str
	année : int
	bord_N : str
	bord_O : str
	bord_S : str
	bord_E : str
#	cultures : int #nombre de culture associée
	culture_bute : list
	culture_entre_bute : list
	

Cultures = [	Parcelle('Parcelle0', 2018, 'Lavande', 'Lavande', 'Tanaisie', 'Lavande', ['Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre'], ['Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Vide']),
		Parcelle('Parcelle0', 2019, 'Lavande', 'Lavande', 'Tanaisie', 'Lavande', ['Ail', 'Oignon', 'Ail', 'Oignon'], ['Vide', 'Ail', 'Oignon', 'Ail', 'Oignon']),
		Parcelle('Parcelle0', 2020, 'NA', 'NA','Romarin', 'Sauge', ['Fève', 'Fève', 'Fève', 'Fève'], ['Vide', 'Fève', 'Fève', 'Fève', 'Vide']),
		Parcelle('Parcelle0', 2021, 'Lavande', 'Lavande', 'Tanaisie', 'Lavande', ['Courgette', 'Courgette', 'Courgette', 'Courgette'], ['Courgette', 'Courgette', 'Courgette', 'Courgette', 'Vide']),
		Parcelle('Parcelle0', 2022, 'Lavande', 'Lavande', 'Tanaisie', 'Lavande', ['Ail', 'Oignon', 'Ail', 'Oignon'], ['Vide', 'Ail', 'Oignon', 'Ail', 'Oignon']),
		Parcelle('Parcelle0', 2023, 'NA', 'NA', 'NA', 'NA', ['Échalotte', 'Vide', 'Vide', 'Vide'], ['Vide', 'Vide', 'Vide', 'Vide', 'Vide']),
		Parcelle('Parcelle1', 2019, 'NA', 'NA','Romarin', 'Sauge', ['Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre'], ['Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre']),
		Parcelle('Parcelle1', 2020, 'NA', 'NA','Romarin', 'Sauge', ['Ail', 'Oignon', 'Ail', 'Oignon'], ['Vide', 'Ail', 'Oignon', 'Ail', 'Oignon']),
		Parcelle('Parcelle1', 2021, 'NA', 'NA', 'NA', 'Lavande', ['Fève', 'Fève', 'Fève', 'Fève'], ['Vide', 'Fève', 'Fève', 'Fève', 'Vide']),
		Parcelle('Parcelle1', 2022, 'NA', 'NA', 'NA', 'Lavande', ['Vide', 'Vide', 'Vide', 'Vide'], ['Vide', 'Vide', 'Vide', 'Vide', 'Vide']),
		Parcelle('Parcelle1', 2023, 'NA', 'NA', 'NA', 'NA', ['Vide', 'Vide', 'Vide', 'Vide'], ['Vide', 'Vide', 'Vide', 'Vide', 'Vide']),
		Parcelle('Parcelle2', 2020, 'NA', 'NA','Romarin', 'Sauge', ['Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre'], ['Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Vide']),
		Parcelle('Parcelle2', 2021, 'NA', 'NA','Romarin', 'Sauge', ['Ail', 'Oignon', 'Ail', 'Oignon'], ['Vide', 'Ail', 'Oignon', 'Ail', 'Oignon']),
		Parcelle('Parcelle2', 2022, 'NA', 'NA','Romarin', 'Sauge', ['Fève', 'Vide', 'Fève', 'Vide'], ['Fève', 'Oseille', 'Fève', 'Vide', 'Fraise']),
		Parcelle('Parcelle2', 2023, 'NA', 'NA','Romarin', 'Sauge', ['Ciboulette', 'Coriandre', 'Menthe', 'Vide'], ['Salade', 'Vide', 'Vide', 'Vide', 'Vide']),
		Parcelle('Parcelle3', 2021, 'NA', 'NA', 'NA', 'NA', ['Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre'], ['Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Pomme de terre', 'Vide']),
		Parcelle('Parcelle3', 2022, 'NA', 'NA', 'NA', 'NA', ['Oignon', 'Oignon', 'Ail', 'Ail'], ['Oignon', 'Ail', 'Fraise', 'Oignon', 'Ail']),
		Parcelle('Parcelle3', 2023, 'NA', 'NA', 'NA', 'NA', ['Fève', 'Fève', 'Fève', 'Fève'], ['Oseille', 'Oseille', 'Fraise', 'Oseille', 'Oseille']),
		Parcelle('Parcelle4', 2022, 'Sauge , Forsythia', 'Thym', 'Lavande , ', 'Thym', ['Pomme de terre','Pomme de terre', 'Pomme de terre', 'Pomme de terre'], ['Navet', 'Betterave', 'Fève', 'Navet', 'Vide']),
		Parcelle('Parcelle4', 2023, 'Sauge , Forsythia', 'Thym', 'Lavande , ', 'Thym', ['Ail', 'Oignon', 'Échalote', 'Vide'], ['Vide', 'Betterave', 'Vide', 'Vide', 'Vide']),
		Parcelle('Parcelle5', 2023, 'NA', 'NA', 'NA', 'NA', ['Vide', 'Vide', 'Vide', 'Vide'], ['Vide', 'Vide', 'Vide', 'Vide', 'Vide'])]


année_date = int(time.strftime('%Y', time.localtime()))
print(année_date, type(année_date), année_date-1)

class champ(Gtk.Window) :
	"""Modélisation d'un champ aux parcelles et planches rectangulaires"""
	def __init__(self) :
		super().__init__() #Initialisation de la fenetre principale
		self.set_title("Culture")
		self.set_size_request(width=366, height=404)
		self.champ_disp()
	def champ_disp(self) :
		self.notebook = Gtk.Notebook(show_tabs=True)
		self.notebook.set_scrollable(True)
		self.add(self.notebook)
		Champ0 = Gtk.Box(spacing=6) #Initialisation de la première Box
		Champ0.set_orientation(Gtk.Orientation.VERTICAL)
		Champ0.set_margin_bottom(6)
		Champ0.set_margin_right(6)
		Champ0.set_margin_left(6)
		Champ0.set_margin_top(6)
		self.notebook.append_page(Champ0, Gtk.Label(label="Champ"))
		parcelles_champ = [] #Superposition des Boxs 
		for i in range(0, 3) : #Quantité de parcelle x 2
			parcelles_champ.append(f"Parcelle{i}_{i}")
			parcelles_champ[i] = Gtk.Box(spacing=6)
			parcelles_champ[i].set_orientation(Gtk.Orientation.HORIZONTAL)
			Champ0.pack_start(parcelles_champ[i], True, True, 0)
		self_box = [] # Enchainement des boxs (parcelles)
		box = []
		global Champ00_list, hbox_list # Listes pour fermer les onglets
		Champ00_list = []
		hbox_list = []
		for i in range (0, 6) : # Initialisation des boxs (3 x 2 parcelles)
			self_box.append(f"self.box{i}")
			box.append(f"Parcelle{i}")
			self_box[i] = Gtk.Box()
			button = Gtk.Button()
			Gtk.Button.set_label(button, box[i])
			button.connect("clicked", self.on_open_clicked)
			self_box[i].pack_start(button, True, True, 0)			
		for i, j in enumerate(self_box) : # 3 x 2 parcelles
			if i <= 1 :
				parcelles_champ[0].pack_start(self_box[i], True, True, 0)
			elif i <= 3 :
				parcelles_champ[1].pack_start(self_box[i], True, True, 0)
			else :
				parcelles_champ[2].pack_start(self_box[i], True, True, 0)
	def parcelle_carree(self, Champ00, hbox, lab, n, bn, be, bs, bo, cult_bute, cult_entre_bute):
		"""Modélisation de planches rectilignes avec bordures

		Paramètres :

		self : str
			Conteneur parent
		Champ00 : str
			Onglet, fenêtre parcelle
		hbox : str
			Gestion des onglets
		lab : str
			Label de l'onglet ouvert
		n : int
			Nombre de planches
		"""
		Champ00 = Gtk.Box(spacing=6) # Conteneur
		Champ00.set_border_width(6)
		Champ00.set_valign(Gtk.Align.CENTER)
		Champ00.set_halign(Gtk.Align.CENTER)
		Champ00.set_orientation(Gtk.Orientation.VERTICAL)
		img = Gtk.Image.new_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.BUTTON) # Affichage des onglets
		btn = Gtk.Button()
		btn.set_image(img)
		btn.set_relief(Gtk.ReliefStyle.NONE)
		btn.connect("clicked", self.close_tab)
		hbox = Gtk.HBox()
		hbox.pack_start(Gtk.Label(label=lab), True, True, 3)
		hbox.pack_end(btn, False, False, 0)
		hbox.show_all()
		self.notebook.append_page(Champ00, hbox)
		Gtk.Notebook.set_tab_detachable(self.notebook, Champ00, True)
		Gtk.Notebook.set_tab_reorderable(self.notebook, Champ00, True)
		Champ00_list.append(Champ00) # Supression des onglets
		hbox_list.append(hbox) 
	# BordureNord Centre BordureSud
		Champ01 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
		Champ00.pack_start(Champ01, False, False, 0)
		button = Gtk.Button.new_with_label(bn)
		Champ01.pack_start(button, True, True, 0)
		Champ02 = Gtk.Box(spacing=0)
		Champ02.set_orientation(Gtk.Orientation.HORIZONTAL)
		Champ00.pack_start(Champ02, True, False, 0)
		Champ03 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
		Champ00.pack_start(Champ03, False, False, 0)
		button = Gtk.Button.new_with_label(bs)
		Champ03.pack_end(button, True, True, 0)
	# BordureOuest Centre BordureEst
		Champ002 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
		Champ002.set_border_width(6)
		Champ02.add(Champ002)
		label = Gtk.Label()
		label.set_text(be)
		label.set_angle(90)
		button = Gtk.Button()
		button.add(label)
		Champ002.pack_start(button, True, True, 0)
		Champ003 = Gtk.Box(spacing=0)
		Champ003.set_orientation(Gtk.Orientation.VERTICAL)
		Champ02.pack_start(Champ003, False, False, 0)
		Champ004 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
		Champ004.set_border_width(6)
		Champ02.add(Champ004)
		label = Gtk.Label()
		label.set_text(bo)
		label.set_angle(90)
		button = Gtk.Button()
		button.add(label)
		Champ004.pack_start(button, True, True, 0)
	# Association de culture x2 ou x8	
#		css_provider = Gtk.CssProvider()
#		css_provider.load_from_path("theme.css")
#		Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
		box = [] 
		iter_pl_b = iter_pl_eb = 0
		for i in range (0, n) : #Initialisation des boxs
			box.append(f"Champ000{i}")
			box[i] = Gtk.Box(spacing=2)
			box[i].set_orientation(Gtk.Orientation.VERTICAL)
			Champ003.pack_start(box[i], True, False, 0)
			outerbox = Gtk.Box(spacing=2, orientation=Gtk.Orientation.VERTICAL)
			Champ003.add(outerbox)
			Champ003.popover = Gtk.Popover()
			vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
			info_button = Gtk.Button(label="Infos")
			hist_button = Gtk.Button(label="Historique")
			vbox.pack_start(info_button, False, True, 2)
			vbox.pack_start(hist_button, False, True, 2)
			vbox.show_all()
			Champ003.popover.add(vbox)
			Champ003.popover.set_position(Gtk.PositionType.BOTTOM)
			label = Gtk.Label("Vide")
			if ( i%2 != 0 ) : #remplissage des butes
				label = Gtk.Label(cult_bute[iter_pl_b])
				iter_pl_b += 1
#				label.set_name('label_planche') #css
			elif ( i%2 == 0 )  : #remplissage des entre_butes
				label = Gtk.Label(cult_entre_bute[iter_pl_eb])
				iter_pl_eb += 1
			button = Gtk.MenuButton(label=None, popover=Champ003.popover)
			button.add(label)
			box[i].add(button)
			info_button.connect("clicked", self.dial_tab)
		Area0 = Gtk.DrawingArea()
		Area0.set_size_request(250, 0)
		Champ003.add(Area0)
	def on_open_clicked(self, button) : # Afficher les planches d'une parcelle
		Champ00 = hbox =  "" #gestion fenetre GTK.
		cult_bute = cult_entre_bute = [] #gestion des données (Réinitialisation)
		for culture_ in sorted(Cultures) :
			if ( culture_.année == année_date ) and ( button.get_label() == culture_.nom ) :
				bn = culture_.bord_N
				be = culture_.bord_E
				bs = culture_.bord_S
				bo = culture_.bord_O	
				cult_bute = culture_.culture_bute
				cult_entre_bute = culture_.culture_entre_bute
				Champ00 =  "Champ00" + culture_.nom 
				hbox = "hbox" + culture_.nom 
			elif ( culture_.année == année_date-1 ) and ( button.get_label() == culture_.nom ) :
				bn = culture_.bord_N
				be = culture_.bord_E
				bs = culture_.bord_S
				bo = culture_.bord_O	
				cult_bute = culture_.culture_bute
				cult_entre_bute = culture_.culture_entre_bute
				Champ00 =  "Champ00" + culture_.nom 
				hbox = "hbox" + culture_.nom 
		self.parcelle_carree(Champ00, hbox, button.get_label(), 9, bn, be, bs, bo, cult_bute, cult_entre_bute)	
		self.show_all()
		self.notebook.set_current_page(len(hbox_list))
	def close_tab(self, btn) : # Fermer l'onglet parcelle
		tab = btn.get_parent()
		for i in range (0, len(hbox_list)) :
			try :
				if tab == hbox_list[i] :
					Champ00_list[hbox_list.index(tab)].destroy()
					Champ00_list.remove(Champ00_list[hbox_list.index(tab)])
					hbox_list.remove(hbox_list[hbox_list.index(tab)])
			except IndexError :
				pass
	def dial_tab(self, button) :
		dialog = Gtk.MessageDialog(	transient_for=self, 
						flags=0, 
						message_type=Gtk.MessageType.INFO, 
						buttons=Gtk.ButtonsType.OK, 
						text="This is an info dial")
		dialog.format_secondary_text('second explanation')
		dialog.run()
		dialog.destroy()


disp = champ()
disp.connect("destroy", Gtk.main_quit)
disp.show_all()
Gtk.main()

Et l’analyse de données là :

#!/usr/bin/env python3

# Le dictionnaire Plantes et la liste Saisons de ce script sont valables pour les cultures sur le 45ème parallèle de l'hémisphère nord.

import time

from plantes import *  #les deux @dataclass du code au dessus
from dataclasses import dataclass, asdict


année_date = int(time.strftime('%Y', time.localtime()))

Saisons = ('Hiver2', 'Hiver3', 'Printemps1', 'Printemps2', 'Printemps3', 'Été1', 'Été2','Été3', 'Automne1', 'Automne2', 'Autonme3', 'Hiver1', 'NA')



def rechercher_saison():
	""" Fonction explicite """
	print('Nous sommes le', time.strftime('%d/%m/%Y', time.localtime()))
	global Saison
	mois_date = int(time.strftime('%m', time.localtime()))
	jours_date = int(time.strftime('%d', time.localtime()))

	if 0 < mois_date <= 3 :
		if (mois_date == 3) and (jours_date < 20):
			print('C\'est la fin de l\'hiver. Il faut surveiller la floraison des saules ou des forsythias, c\'est qu\'il fait 6°C et que le potager peut commencer à se remplir')
			Saison = 'Hiver3'
		elif (mois_date == 3) and (jours_date >= 20):
			print('Voila le printemps, les narcices fleurissent-ils ? ;)')
			Saison = 'Printemps1'
		else:
			print('C\'est l\'hiver. Les arbres sont taillés et les troups dans les haies comblés par de nouveaux plants.')
			Saison = 'Hiver2'

	elif 3 < mois_date <= 6 :
		if (mois_date == 6) and (jours_date < 20):
			print('C\'est la fin du printemps, prêt pour les plantations ?')
			Saison = 'Printemps3'
		elif (mois_date == 6) and (jours_date >= 20):
			print('Voila l\'été, il va falloir arroser.')
			Saison = 'Été1'
		else:
			print('C\'est le printemps. Les narcices, les lilas, les cerisiers et les rosiers vont fleurir, à garder à l\'œil.')
			Saison = 'Printemps2'

	elif 6 < mois_date <= 9 :
		if (mois_date == 9) and (jours_date < 23):
			print('C\'est la fin de l\'été. L\'engrais vert est-il en place ?')
			Saison = 'Été3'
		elif (mois_date == 9) and (jours_date >= 23):
			print('Voila l\'automne, les feuilles vont tomber')
			Saison = 'Automne1'
		else:
			print('C\'est l\'été, en espérant que chacun fasse le plein pour les petits plats d\'hiver.')
			Saison = 'Été2'

	elif 9 < mois_date <= 12 :
		if (mois_date == 12) and (jours_date < 21):
			print('C\'est la fin de l\'automne. Tout ce qui doit l\'être est à l\'abris du gel ?')
			Saison = 'Automne3'
		elif (mois_date == 12) and (jours_date >= 21):
			print('Voila l\'hiver, va-t-il neiger cette année ?')
			Saison = 'Hiver1'
		else:
			print('Prêt à recommencer au printemps ?')
			Saison = 'Automne2'



def proposer_activite():
	global Asemer, Aplanter
	Asemer, Aplanter = [], []
	for plante in sorted(Plantes) :
		SmDeb, SmFin = plante.début_semi, plante.fin_semi		#Récupérer dates Semis dans dictionnaire Plantes
		if Saisons.index(SmDeb) == Saisons.index(SmFin):
			pass
		elif Saisons.index(SmDeb) < Saisons.index(SmFin):
			track = Saisons.index(SmDeb)
			while track <= Saisons.index(SmFin):
				if Saisons[track] == Saison :
#					print('Vous pouvez semer des ', plante.nom, 's')
					Asemer.append(plante.nom)
				track += 1
		elif Saisons.index(SmDeb) > Saisons.index(SmFin):
			track = Saisons.index(SmDeb)
			while track <= 12:
				if Saisons[track] == Saison :
#					print('Vous pouvez semer des ', plante.nom, 's')	
					Asemer.append(plante.nom)		
				track += 1
			track = 0
			while track <= Saisons.index(SmFin):
				if Saisons[track] == Saison :
#					print('Vous pouvez semer des ', plante.nom, 's')	
					Asemer.append(plante.nom)		
				track += 1

#	for plante in sorted(Plantes) :
		PltDeb, PltFin = plante.début_plantation, plante.fin_plantation	#Récupérer dates Plantations dans dictionnaire Plantes
		if Saisons.index(PltDeb) == Saisons.index(PltFin):
			pass
		elif Saisons.index(PltDeb) < Saisons.index(PltFin):
			track = Saisons.index(PltDeb)
			while track <= Saisons.index(PltFin):
				if Saisons[track] == Saison :
#					print('Vous pouvez planter des ', plante.nom, 's')
					Aplanter.append(plante.nom)
				track += 1
		elif Saisons.index(PltDeb) > Saisons.index(PltFin):
			track = Saisons.index(PltDeb)
			while track <= 12:
				if Saisons[track] == Saison :
#					print('Vous pouvez planter des ', plante.nom, 's')
					Aplanter.append(plante.nom)			
				track += 1
			track = 0
			while track <= Saisons.index(PltFin):
				if Saisons[track] == Saison :
#					print('Vous pouvez planter des ', plante.nom, 's')	
					Aplanter.append(plante.nom)		
				track += 1




def choisir_plante():
	global Objet
	Objet = 0
	Afaire = Asemer + Aplanter
	while ((Objet in Afaire) == False) :
		print("Saisir le nom d'une plante à semer ou à planter parmi celles-ci : ", Asemer, Aplanter)
		Objet = input()
		if ((Objet in Asemer) == False) and ((Objet in Aplanter) == False) :
			print('Saisie hors liste, recommencer : ')
	for plante in sorted(Plantes) :
		if plante.nom == Objet :
			print('L(\')e ', Objet, "\b, est un(e)", plante.famille, "aux racines", plante.racine, "\bs dont vous récolterez (peut-être) la(les)", plante.consommé, '\b.') 






def HistRotation(n) : 
	"""Création du dictionnaire pour la recherche de parcelles

	Paramètres :

	n : int
		Nombres d'années
	"""
	global dic_hist, dic_plant	
	dic_hist = {}
	dic_plant = {}
	for culture in Cultures :
		if année_date - culture.année < n :
			tup_key = ((astuple(culture)[0],astuple(culture)[1]))
			tup_value = ((astuple(culture)[6] + astuple(culture)[7]))
			dic_hist[tup_key] = tup_value
	for plante in Plantes :
		tup_key = (astuple(plante)[0])
		tup_value = (astuple(plante)[1])
		dic_plant[tup_key] = tup_value



def chercher_planches():
	n=0
	ListTemp = []	#(Parcelles où il ne faut pas cultiver)
	HistRotation(5)
	print(dic_hist, '\n\n', dic_plant)
	Historiques = dic_hist
	Plantes = dic_plant
	print(list(Historiques.values())[5][8])

	while n < len(list(Historiques.values())):
		for i in range(0, 9) :   # 4 butes et 5 entre butes
			if list(Historiques.values())[n][i] in list(Plantes.keys()):	#
				if (Plantes[list(Historiques.values())[n][i]][0]) == Plantes[Objet][0]:	#Comparer la famille plantée sur une parcelle avec la famille de la plante à travailler
					ListTemp.append(((list(Historiques.items())[n][0][0]),i))	#Ajout dans la liste, des parcelles ayant eu des cultures de la même famille 
		n += 1
	n=0
	ListTempFinal = []

	while n < len(Historiques.keys()) :	#Sélectionner les parcelles n'ayant pas été recouverte par la famille à travailler. 
		for i in range(0, 9) :
			if ((list(Historiques.keys())[n][0],i) not in list(ListTemp)) and ((list(Historiques.keys())[n][0],i) not in list(ListTempFinal)): #Évite les doublons
				ListTempFinal.append((list(Historiques.keys())[n][0],i))
		n += 1
#	if ListTemp == ['Parcelle', (-1000, -1000)] :
#		creer_parcelle()
	else :
		print('Vous pouvez planter ou semer', Objet, 'sur les (la) parcelles :', list(ListTempFinal))



chercher_planches()

Pour proposer le service plus largement j’aimerais proposer une visualisation avec des boutons en camembert, pour des parcelles rondes. Pour enfin réfléchir à des visualisations de parcs personnalisés et précises.

Cairo donc, pour continuer à développer la modélisation du visuel ?

Par la suite, aussi, « Tree and List widget » de GTK pour afficher mais surtout pour manipuler les données ? Même si pour le moment Python s’occupe de la manipulation et de l’analyse comme il faut :).

Une étape sera aussi la mise en route de l’application, pour saisir le suivi, faciliter le contrôle, et comparer les rendements.