1477 lines
58 KiB
Python
Executable File
1477 lines
58 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Application de gestion de cave à cigares
|
|
WTFPL @ Jérôme LAUNAY
|
|
"""
|
|
import locale
|
|
import ntpath
|
|
import os
|
|
import sys
|
|
import sqlite3
|
|
import gi
|
|
from mescigaresdb import mescigaresdb
|
|
|
|
gi.require_version('Gtk', '3.0')
|
|
try:
|
|
from gi.repository import Gtk as gtk, GdkPixbuf
|
|
except ImportError:
|
|
print("GTK (PyGI) n'est pas installé")
|
|
sys.exit(1)
|
|
|
|
|
|
__CONFDIR__ = os.path.dirname(os.path.realpath("MesCigares.py"))
|
|
|
|
"""
|
|
Class MesCigares
|
|
"""
|
|
|
|
|
|
class MesCigares(object):
|
|
|
|
@classmethod
|
|
def on_window_main_destroy(cls, widget, data=None):
|
|
del widget, data
|
|
db.close()
|
|
gtk.main_quit()
|
|
|
|
@classmethod
|
|
def on_gtk_quit_activate(cls, menuitem, data=None):
|
|
del menuitem, data
|
|
db.close()
|
|
gtk.main_quit()
|
|
|
|
def on_gtk_about_activate(self, menuitem, data=None):
|
|
del menuitem, data
|
|
self.aboutdialog.run()
|
|
self.aboutdialog.hide()
|
|
|
|
# Mise à jour de la quantité en fonction du cigare choisi dans la liste
|
|
# déroulante
|
|
def on_cbchoixcigqte_changed(self, objt, data=None):
|
|
del objt, data
|
|
item = self.cbchoixcigqte.get_active()
|
|
|
|
# Si aucune séléction, pas besoin de traitement.
|
|
if item == -1:
|
|
return
|
|
|
|
model = self.cbchoixcigqte.get_model()
|
|
idc = model[item]
|
|
|
|
cursor = db.cursor()
|
|
qte = cursor.execute("Select quantite from stocks " +
|
|
"where id_cigare = '{0}'"
|
|
.format(idc[0])).fetchone()[0]
|
|
cursor.close()
|
|
|
|
self.scaleqte.set_value(int(qte))
|
|
|
|
# Permet d'alimenter la treeview avec la liste des cigares
|
|
def loadtreeview(self):
|
|
if len(self.table) != 0:
|
|
self.table.clear()
|
|
|
|
cursor = db.cursor()
|
|
cursor.execute('Select c.designation, m.libelle, ' +
|
|
'mo.module,m.provenance,s.quantite,' +
|
|
'c.id from cigares c inner join modules mo on ' +
|
|
'mo.id = c.module inner join marques m on m.id = ' +
|
|
'c.marque inner join stocks s on s.id_cigare = c.id')
|
|
rs = cursor.fetchall()
|
|
|
|
cursor.execute("select avg(d.note), max(d.note),id_cigare " +
|
|
"from degustation d group by id_cigare")
|
|
rs2 = cursor.fetchall()
|
|
# top = 5 premier items de la liste par meilleure note [2] pour la note
|
|
# seulement
|
|
top = sorted(rs2, reverse=True)[0:5]
|
|
print(top)
|
|
# top5 = juste les id
|
|
top5 = list()
|
|
for i in top:
|
|
top5.append(i[2])
|
|
|
|
cursor.execute("select min(prix),id_cigare from achats " +
|
|
"group by id_cigare")
|
|
rs3 = cursor.fetchall()
|
|
|
|
cursor.execute("select commentaires, id_cigare from degustation " +
|
|
"group by id_cigare")
|
|
rs4 = cursor.fetchall()
|
|
|
|
for item in rs:
|
|
itr = self.table.append()
|
|
# Si le nombre de cigares est = 0 on affiche la ligne en rouge
|
|
# clair
|
|
if item[4] == 0:
|
|
color = '#F3B9B9' # rouge clair
|
|
else:
|
|
color = '#FFFFFF' # blanc
|
|
|
|
# On regarde si l'enregistrement est dans le top 5
|
|
try:
|
|
index = top5.index(item[5])
|
|
except Exception:
|
|
index = 9
|
|
# Et on affecte la couleur (dégradé de vert)
|
|
if index == 0:
|
|
topcolor = '#8FFF80'
|
|
if index == 1:
|
|
topcolor = '#A4FF97'
|
|
if index == 2:
|
|
topcolor = '#BFFFB7'
|
|
if index == 3:
|
|
topcolor = '#CDFFC7'
|
|
if index == 4:
|
|
topcolor = '#DBFFD7'
|
|
if index == 9:
|
|
topcolor = color
|
|
|
|
self.table.set(itr, 0, item[0], 1, item[1], 2, item[2].title(), 3,
|
|
item[3].title(), 4, item[4], 5, item[5], 11,
|
|
color, 12, topcolor)
|
|
# Il n'y a pas de outer join en sqlite du coup je suis obligé
|
|
# d'alimenter les notes à part
|
|
for item2 in rs2:
|
|
itr = self.table.get_iter_first()
|
|
while itr is not None:
|
|
# itr, 5 = id de la table cigare (colonne 4 invisible),
|
|
# item2[2] = id_cigare table degustation
|
|
if self.table.get_value(itr, 5) == item2[2]:
|
|
self.table.set(itr, 6, item2[1], 7, item2[0])
|
|
itr = self.table.iter_next(itr)
|
|
for item3 in rs3:
|
|
itr = self.table.get_iter_first()
|
|
while itr is not None:
|
|
# itr, 5 = id de la table cigare (colonne 4 invisible),
|
|
# item3[1] = id_cigare table achats
|
|
if self.table.get_value(itr, 5) == item3[1]:
|
|
# .2f pour 2 decimales
|
|
it = '%.2f' % item3[0]
|
|
self.table.set(itr, 8, it + " €", 10, float(item3[0]))
|
|
itr = self.table.iter_next(itr)
|
|
for item4 in rs4:
|
|
itr = self.table.get_iter_first()
|
|
while itr is not None:
|
|
# itr, 4 = id de la table cigare (colonne 4 invisible),
|
|
# item4[1] = id_cigare table achats
|
|
if self.table.get_value(itr, 5) == item4[1]:
|
|
self.table.set(itr, 9, item4[0])
|
|
itr = self.table.iter_next(itr)
|
|
|
|
# On calcule le nombre de cigares et le prix
|
|
nbcigares = 0
|
|
montant = 0
|
|
itr = self.table.get_iter_first()
|
|
while itr is not None:
|
|
nbcigares += int(self.table.get_value(itr, 4))
|
|
if self.table.get_value(itr, 8) is not None:
|
|
montant += float(self.table.get_value(itr, 10)) * \
|
|
int(self.table.get_value(itr, 4))
|
|
itr = self.table.iter_next(itr)
|
|
montant = float('%.2f' % montant)
|
|
|
|
# On calcule le prix total
|
|
montant_total = 0
|
|
cursor.execute("select prix, quantite from achats")
|
|
rs = cursor.fetchall()
|
|
|
|
for item in rs:
|
|
montant_total += (item[0] * item[1])
|
|
montant_total = float('%.2f' % montant_total)
|
|
|
|
cursor.close()
|
|
# On affiche le nombre de résultats trouvés sur la barre de statut
|
|
if nbcigares == 0:
|
|
self.statusbar.push(self.context_id, "Aucun cigare dans la base")
|
|
else:
|
|
self.statusbar.push(
|
|
self.context_id,
|
|
str(nbcigares) +
|
|
" cigares dans la base pour un montant de " +
|
|
str(montant) +
|
|
"€ - (" +
|
|
str(montant_total) +
|
|
"€ depuis le début)")
|
|
|
|
# Permet l'affichage de la photo du cigare séléctionné dans la zone
|
|
# rétractable
|
|
def affiche_preview(self):
|
|
|
|
cursor = db.cursor()
|
|
photo = cursor.execute(
|
|
"select photo from photos where id_cigare = '{0}'".format(
|
|
self.treeview_selection[5]))
|
|
|
|
try:
|
|
photo = cursor.fetchone()[0]
|
|
except TypeError:
|
|
photo = None
|
|
cursor.close()
|
|
# Si aucune photo n'existe pour ce cigare
|
|
if photo is None:
|
|
photo = __CONFDIR__ + "/images/no_photo.jpg"
|
|
self.image_preview.set_from_file(photo)
|
|
else:
|
|
if os.path.exists(photo):
|
|
pixbuf = GdkPixbuf.Pixbuf().new_from_file(photo)
|
|
# Si l'image est plus grande que la fenêtre on re-dimensionne
|
|
new_size = self.window.get_size()[0] - 20
|
|
if pixbuf.get_width() > new_size:
|
|
w, h = pixbuf.get_width(), pixbuf.get_height()
|
|
pixbuf = pixbuf.scale_simple(
|
|
new_size, new_size * h / w, GdkPixbuf.InterpType.HYPER)
|
|
# Si la largeur de l'image est plus petite que la hauteur on
|
|
# fait pivoter
|
|
if pixbuf.get_width() < pixbuf.get_height():
|
|
pixbuf = pixbuf.rotate_simple(270)
|
|
# Si la hauteur est dépasse la moitié de la fenêtre principale,
|
|
# on re-dimensionne
|
|
if pixbuf.get_height() > self.window.get_size()[1] / 2:
|
|
w, h = pixbuf.get_width(), pixbuf.get_height()
|
|
pixbuf = pixbuf.scale_simple(
|
|
(w * self.window.get_size()[1] / 2) / h,
|
|
self.window.get_size()[1] / 2,
|
|
GdkPixbuf.InterpType.HYPER)
|
|
self.image_preview.set_from_pixbuf(pixbuf)
|
|
else:
|
|
# Si la photo n'existe plus ou a été renommée
|
|
photo = __CONFDIR__ + "/images/bad_photo.jpg"
|
|
self.image_preview.set_from_file(photo)
|
|
self.imgpath = photo
|
|
|
|
# Si la fenêtre principale change de taille on re-dimensionne le preview
|
|
# aussi
|
|
def on_window_main_check_resize(self, widget):
|
|
del widget
|
|
# print(self.window.get_size()[1],self.window_last_h)
|
|
if abs(self.window_last_h - self.window.get_size()[1] > 40) | \
|
|
abs(self.window_last_w - self.window.get_size()[0]) > 5:
|
|
self.window_last_h = self.window.get_size()[1]
|
|
self.window_last_w = self.window.get_size()[0]
|
|
|
|
if not os.path.exists(self.imgpath):
|
|
return
|
|
pixbuf = GdkPixbuf.Pixbuf().new_from_file(self.imgpath)
|
|
# Si l'image est plus grande que la fenêtre on re-dimensionne
|
|
new_size = self.window.get_size()[0] - 20
|
|
if pixbuf.get_width() > new_size:
|
|
w, h = pixbuf.get_width(), pixbuf.get_height()
|
|
pixbuf = pixbuf.scale_simple(
|
|
new_size, new_size * h / w, GdkPixbuf.InterpType.HYPER)
|
|
# Si la largeur de l'image est plus petite que la hauteur on fait
|
|
# pivoter
|
|
if pixbuf.get_width() < pixbuf.get_height():
|
|
pixbuf = pixbuf.rotate_simple(270)
|
|
# Si la hauteur est dépasse la moitié de la fenêtre principale, on
|
|
# re-dimensionne
|
|
if pixbuf.get_height() > self.window.get_size()[1] / 2:
|
|
w, h = pixbuf.get_width(), pixbuf.get_height()
|
|
pixbuf = pixbuf.scale_simple(
|
|
(w * self.window.get_size()[1] / 2) / h,
|
|
self.window.get_size()[1] / 2,
|
|
GdkPixbuf.InterpType.HYPER)
|
|
self.image_preview.set_from_pixbuf(pixbuf)
|
|
|
|
# Dès qu'une ligne est séléctionnée sur la treeview on enregistre les
|
|
# valeurs
|
|
def on_twcig_row_activated(self, widget):
|
|
del widget
|
|
selection = self.tree.get_selection()
|
|
(model, itr) = selection.get_selected()
|
|
if itr is not None:
|
|
self.treeview_selection.clear()
|
|
self.treeview_selection.append(model.get_value(itr, 0))
|
|
self.treeview_selection.append(model.get_value(itr, 1))
|
|
self.treeview_selection.append(model.get_value(itr, 2))
|
|
self.treeview_selection.append(model.get_value(itr, 3))
|
|
self.treeview_selection.append(model.get_value(itr, 4))
|
|
self.treeview_selection.append(model.get_value(itr, 5))
|
|
self.treeview_selection.append(model.get_value(itr, 6))
|
|
self.treeview_selection.append(model.get_value(itr, 7))
|
|
# for i in range(0,6):
|
|
# print(model.get_value(iter, i))
|
|
# print("\n")
|
|
# Et on met à jour l'affichage du preview
|
|
self.affiche_preview()
|
|
|
|
def get_libelle_quantite(self):
|
|
selection = self.tree.get_selection()
|
|
(model, itr) = selection.get_selected()
|
|
if itr is not None:
|
|
lib = model.get_value(itr, 0)
|
|
qtee = model.get_value(itr, 5)
|
|
return lib, qtee
|
|
|
|
# Changer de base de données
|
|
def on_bdd_change_activate(self, widget):
|
|
del widget
|
|
filter = gtk.FileFilter()
|
|
filter.set_name("Bases sqlite")
|
|
filter.add_mime_type("application/octet-stream")
|
|
filter.add_pattern("*.db")
|
|
self.filechooser_bdd.add_filter(filter)
|
|
self.filechooser_bdd.set_current_folder(__CONFDIR__ + "/bdd/")
|
|
self.filechooser_bdd.run()
|
|
self.loadtreeview()
|
|
self.filechooser_bdd.hide()
|
|
|
|
# Double clic sur le sélécteur de fichiers pour choisir la bdd
|
|
def on_choixbdd_activated(self, widget):
|
|
del widget
|
|
if not os.path.exists(self.filechooser_bdd.get_filename()):
|
|
createdb(self.filechooser_bdd.get_filename())
|
|
setconfig(self.filechooser_bdd.get_filename())
|
|
global db
|
|
db.close()
|
|
db = sqlite3.connect(self.filechooser_bdd.get_filename())
|
|
# On met à jour le titre de la fenêtre
|
|
self.window.set_title("MesCigares - base : {0}".format(
|
|
ntpath.basename(self.filechooser_bdd.get_filename().replace(
|
|
'.db', ''))))
|
|
self.loadtreeview()
|
|
self.filechooser_bdd.hide()
|
|
|
|
# Clic sur le bouton OK du sélécteur de fichiers pour choisir l'image
|
|
def on_btchooseokbdd_clicked(self, widget):
|
|
del widget
|
|
if not os.path.exists(self.filechooser_bdd.get_filename()):
|
|
createdb(self.filechooser_bdd.get_filename())
|
|
# changer la conf
|
|
setconfig(self.filechooser_bdd.get_filename())
|
|
global db
|
|
db.close()
|
|
db = sqlite3.connect(self.filechooser_bdd.get_filename())
|
|
self.loadtreeview()
|
|
|
|
# Ajouter une image pour un cigare
|
|
def on_inserer_image_activate(self, widget):
|
|
del widget
|
|
self.filechooser_image.run()
|
|
self.filechooser_image.hide()
|
|
|
|
# Modifier/Supprimer une note de degustation
|
|
def on_modsupp_degustation_activate(self, widget):
|
|
del widget
|
|
# On commence par vider la liststore_date_modif_degustation (2ème
|
|
# utilisation)
|
|
if len(self.lstore_dmodif_degust) != 0:
|
|
self.lstore_dmodif_degust.clear()
|
|
|
|
cursor = db.cursor()
|
|
# On regarde récupère le commentaire
|
|
cursor.execute("select date,q1,q2,q3,q4,q5,q6,q7,q8,q9,q10," +
|
|
"q11,q12,q13,id,commentaires from degustation " +
|
|
"where id_cigare = " +
|
|
"'{0}'".format(self.treeview_selection[5]))
|
|
rs = cursor.fetchall()
|
|
# si nb = 0 on insère sinon on update la table photos
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
if len(rs) == 0:
|
|
# Afficher un message d'infos pour alerter qu'il faut d'abord faire
|
|
# une dégustation
|
|
self.msgdialog.format_secondary_text(
|
|
"Il n'y a aucune dégustation pour ce cigare")
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
return
|
|
|
|
for item in rs:
|
|
date = item[0]
|
|
q1 = item[1]
|
|
q2 = item[2]
|
|
q3 = item[3]
|
|
q4 = item[4]
|
|
q5 = item[5]
|
|
q6 = item[6]
|
|
q7 = item[7]
|
|
q8 = item[8]
|
|
q9 = item[9]
|
|
q10 = item[10]
|
|
q11 = item[11]
|
|
q12 = item[12]
|
|
q13 = item[13]
|
|
idcomm = item[14]
|
|
commentaire = item[15]
|
|
itr = self.lstore_dmodif_degust.append()
|
|
self.lstore_dmodif_degust.set(
|
|
itr, 0, date, 1, q1, 2, q2, 3, q3, 4, q4, 5, q5, 6,
|
|
q6, 7, q7, 8, q8, 9, q9, 10, q10, 11, q11, 12, q12,
|
|
13, q13, 14, idcomm, 15, commentaire)
|
|
|
|
# On affiche la fenêtre de modification du commentaire
|
|
self.window_modif_degustation.run()
|
|
self.window_modif_degustation.hide()
|
|
# On recharge la treeview
|
|
self.loadtreeview()
|
|
|
|
# Changement sur la liste déroulante de choix de la note de dégustation à
|
|
# modifier/supprimer
|
|
def on_cbchoix_dmodif_degust_changed(self, widget):
|
|
del widget
|
|
index = self.cbchoix_date_modif_degustation.get_active()
|
|
q = list()
|
|
try:
|
|
for i in range(1, 14):
|
|
q.append(self.lstore_dmodif_degust[index][i])
|
|
# idcomm = self.lstore_dmodif_degust[index][14]
|
|
|
|
except IndexError:
|
|
pass
|
|
|
|
try:
|
|
commentaire = self.lstore_dmodif_degust[index][15]
|
|
self.txtview_commentaire_modif.get_buffer().set_text(commentaire)
|
|
except IndexError:
|
|
self.txtview_commentaire_modif.get_buffer().set_text("")
|
|
|
|
# On met la bonne valeur sur les scales
|
|
try:
|
|
self.scaleq1m.set_value(q[0])
|
|
self.scaleq2m.set_value(q[1])
|
|
self.scaleq3m.set_value(q[2])
|
|
self.scaleq4m.set_value(q[3])
|
|
self.scaleq5m.set_value(q[4])
|
|
self.scaleq6m.set_value(q[5])
|
|
self.scaleq7m.set_value(q[6])
|
|
self.scaleq8m.set_value(q[7])
|
|
self.scaleq9m.set_value(q[8])
|
|
self.scaleq10m.set_value(q[9])
|
|
self.scaleq11m.set_value(q[10])
|
|
self.scaleq12m.set_value(q[11])
|
|
self.scaleq13m.set_value(q[12])
|
|
except IndexError:
|
|
self.scaleq1m.set_value(1)
|
|
self.scaleq2m.set_value(1)
|
|
self.scaleq3m.set_value(1)
|
|
self.scaleq4m.set_value(5)
|
|
self.scaleq5m.set_value(2)
|
|
self.scaleq6m.set_value(2)
|
|
self.scaleq7m.set_value(2)
|
|
self.scaleq8m.set_value(1)
|
|
self.scaleq9m.set_value(1)
|
|
self.scaleq10m.set_value(1)
|
|
self.scaleq11m.set_value(1)
|
|
self.scaleq12m.set_value(1)
|
|
self.scaleq13m.set_value(1)
|
|
|
|
# Calcul de la note globale pour modification d'une note de dégustation
|
|
def on_scaleqm_change_value(self, scale, enum, new_value):
|
|
# On récupère les notes
|
|
|
|
note = []
|
|
note.append(int(self.scaleq1m.get_value()))
|
|
note.append(int(self.scaleq2m.get_value()))
|
|
note.append(int(self.scaleq3m.get_value()))
|
|
note.append(int(self.scaleq4m.get_value()))
|
|
note.append(int(self.scaleq5m.get_value()))
|
|
note.append(int(self.scaleq6m.get_value()))
|
|
note.append(int(self.scaleq7m.get_value()))
|
|
note.append(int(self.scaleq8m.get_value()))
|
|
note.append(int(self.scaleq9m.get_value()))
|
|
note.append(int(self.scaleq10m.get_value()))
|
|
note.append(int(self.scaleq11m.get_value()))
|
|
note.append(int(self.scaleq12m.get_value()))
|
|
note.append(int(self.scaleq13m.get_value()))
|
|
|
|
# On affiche la note / 100 temporaire
|
|
self.lbl_note_totale.set_text(
|
|
"Nouvelle note: {0}/100".format(sum(note)))
|
|
|
|
# Validation de la modification d'une dégustation
|
|
def on_btvalid_degustation_clicked(self, widget):
|
|
del widget
|
|
# modifier
|
|
index = self.cbchoix_date_modif_degustation.get_active()
|
|
textbuffer = self.txtview_commentaire_modif.get_buffer()
|
|
start = textbuffer.get_start_iter()
|
|
end = textbuffer.get_end_iter()
|
|
# On récupère le commentaire et on échape les quote pour l'insert SQL
|
|
commentaire = textbuffer.get_text(
|
|
start, end, include_hidden_chars=False).replace("'", "''")
|
|
# On récupère les notes
|
|
note = []
|
|
note.append(int(self.scaleq1m.get_value()))
|
|
note.append(int(self.scaleq2m.get_value()))
|
|
note.append(int(self.scaleq3m.get_value()))
|
|
note.append(int(self.scaleq4m.get_value()))
|
|
note.append(int(self.scaleq5m.get_value()))
|
|
note.append(int(self.scaleq6m.get_value()))
|
|
note.append(int(self.scaleq7m.get_value()))
|
|
note.append(int(self.scaleq8m.get_value()))
|
|
note.append(int(self.scaleq9m.get_value()))
|
|
note.append(int(self.scaleq10m.get_value()))
|
|
note.append(int(self.scaleq11m.get_value()))
|
|
note.append(int(self.scaleq12m.get_value()))
|
|
note.append(int(self.scaleq13m.get_value()))
|
|
|
|
# Le commentaire et l'id
|
|
idcomm = self.lstore_dmodif_degust[index][14]
|
|
id_cigare = self.treeview_selection[5]
|
|
|
|
# On met à jour la base de données
|
|
cursor = db.cursor()
|
|
|
|
req = ("update degustation set commentaires ='{0}',q1='{1}'," +
|
|
"q2='{2}',q3='{3}',q4 ='{4}',q5='{5}',q7='{7}'" +
|
|
",q8='{8}',q9='{9}',q10='{10}',q11='{11}',q12=" +
|
|
"'{12}',q13='{13}',note='{14}' where id_cigare='{15}' " +
|
|
"and id='{16}'")
|
|
|
|
sql = (req.format(commentaire, note[0], note[1], note[2],
|
|
note[3], note[4], note[5], note[6],
|
|
note[7], note[8], note[9], note[10],
|
|
note[11], note[12], sum(note),
|
|
id_cigare, idcomm))
|
|
cursor.execute(sql)
|
|
db.commit()
|
|
cursor.close()
|
|
# Afficher un message d'infos pour confirmer
|
|
self.msgdialog.format_secondary_text(
|
|
"Modification de la dégustation pour " +
|
|
"{0}.\nNouvelle note: {1}".format(
|
|
self.treeview_selection[1], sum(note)))
|
|
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
|
|
# On recharge la liste des cigares
|
|
self.loadtreeview()
|
|
|
|
# Double clic sur le sélécteur de fichiers pour choisir l'image
|
|
def on_filechooser_image_file_activated(self, widget):
|
|
del widget
|
|
self.addimage(self.filechooser_image.get_filename())
|
|
self.filechooser_image.hide()
|
|
|
|
# Clic sur le bouton OK du sélécteur de fichiers pour choisir l'image
|
|
def on_btchooseok_clicked(self, widget):
|
|
del widget
|
|
self.addimage(self.filechooser_image.get_filename())
|
|
|
|
# Mettre à jour la BDD avec le chemin de l'image choisie
|
|
def addimage(self, path):
|
|
|
|
cursor = db.cursor()
|
|
# On regarde s'il y a déjà une photo pour le cigare séléctionné
|
|
req = ("select count(*) from photos where id_cigare ='{0}'"
|
|
.format(self.treeview_selection[5]))
|
|
nb = cursor.execute(req).fetchone()[0]
|
|
# si nb = 0 on insère sinon on update la table photos
|
|
if nb == 0:
|
|
cursor.execute("insert into photos " +
|
|
"(id_cigare, photo) " +
|
|
"values('{0}','{1}')".format(
|
|
self.treeview_selection[5], path))
|
|
else:
|
|
cursor.execute(
|
|
"update photos set photo = '{0}' where id_cigare='{1}'".format(
|
|
path, self.treeview_selection[5]))
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
# Clic droit sur un cigare dans la treeview
|
|
def on_treeview_cigares_button_press_event(self, treeview, event):
|
|
x = int(event.x)
|
|
y = int(event.y)
|
|
if event.button == 3: # Clic droit
|
|
pthinfo = self.tree.get_path_at_pos(x, y)
|
|
if pthinfo is not None:
|
|
path, col, cellx, celly = pthinfo
|
|
self.tree.grab_focus()
|
|
self.tree.set_cursor(path, col, 0)
|
|
# On affiche le popup
|
|
self.popup.show_all()
|
|
self.popup.popup(None, None, None, None, event.button, event.time)
|
|
|
|
def text_edited(self, widget, path, text):
|
|
del widget
|
|
self.liststore[path][1] = text
|
|
|
|
# Mise à jour du listestore si édition d'une cellule
|
|
def cell_edited_callback(self, cellule, path, new_text, col, data=None):
|
|
iter = self.table.get_iter(path)
|
|
if col == 4:
|
|
try:
|
|
self.table.set_value(iter, col, int(new_text))
|
|
self.update_db("id", new_text, tuple(
|
|
self.treeview_selection)[5])
|
|
except ValueError: # Si la quantité saisie n'est pas un entier
|
|
pass
|
|
else:
|
|
self.table.set_value(iter, col, new_text)
|
|
self.update_db("nom", new_text, tuple(self.treeview_selection)[5])
|
|
|
|
# Mise à jour de la BDD si changement dans un cellule
|
|
def update_db(self, operation, new_text, indice):
|
|
cursor = db.cursor()
|
|
# update cigares.designation
|
|
if operation == 'nom':
|
|
sql = (
|
|
"update cigares set designation =" +
|
|
"'{0}' where id = '{1}'".format(
|
|
new_text.replace("'", "''"), str(indice)))
|
|
# print(sql)
|
|
cursor.execute(sql)
|
|
db.commit()
|
|
|
|
# update quantité dans table stock
|
|
if operation == 'id':
|
|
sql = (
|
|
"update stocks set quantite = " +
|
|
"'{0}' where id_cigare='{1}'".format(
|
|
new_text, str(indice)))
|
|
# print(sql)
|
|
cursor.execute(sql)
|
|
db.commit()
|
|
|
|
cursor.close()
|
|
# recharger la treeview
|
|
self.loadtreeview()
|
|
|
|
# Affichage de la fenêtre d'ajout d'un cigare
|
|
def on_gtk_new_activate(self, widget):
|
|
del widget
|
|
# On commence par vider le liststore_marques (2è recherche)
|
|
if len(self.tablemarques) != 0:
|
|
self.tablemarques.clear()
|
|
if len(self.tablemodules) != 0:
|
|
self.tablemodules.clear()
|
|
|
|
cursor = db.cursor()
|
|
|
|
sql = ('Select id,libelle,provenance '
|
|
'from marques order by lower(libelle)')
|
|
|
|
cursor.execute(sql)
|
|
rs = cursor.fetchall()
|
|
# On alimente la combobox des marques
|
|
for item in rs:
|
|
iter = self.tablemarques.append()
|
|
self.tablemarques.set(iter, 0, item[0], 1,
|
|
item[1].title(), 2, item[2].title())
|
|
|
|
sql = ('Select id, module, longueur, diametre, calibre '
|
|
'from modules order by lower(module)')
|
|
|
|
cursor.execute(sql)
|
|
|
|
rs = cursor.fetchall()
|
|
# Et celle des modules
|
|
for item in rs:
|
|
itr = self.tablemodules.append()
|
|
self.tablemodules.set(itr,
|
|
0, item[0],
|
|
1, item[1].title(),
|
|
2, item[2],
|
|
3, item[3],
|
|
4, item[4])
|
|
|
|
cursor.close()
|
|
self.dialog_add_cigare.run()
|
|
self.dialog_add_cigare.hide()
|
|
|
|
# Changement dans la liste déroulante des marques
|
|
def on_cbmarques_changed(self, widget):
|
|
del widget
|
|
index = self.cbmarques.get_active()
|
|
try:
|
|
provenance = self.tablemarques[index][2].title()
|
|
self.lbl_marque.set_text("Terroir: " + provenance)
|
|
except IndexError:
|
|
self.lbl_marque.set_text("")
|
|
|
|
# Changement dans la liste déroulante des modules
|
|
def on_cbmodules_changed(self, widget):
|
|
del widget
|
|
try:
|
|
index = self.cbmodules.get_active()
|
|
longueur = self.tablemodules[index][2]
|
|
diametre = self.tablemodules[index][3]
|
|
calibre = self.tablemodules[index][4]
|
|
self.lbl_module.set_text(
|
|
"{0} mm, diamètre {1}, calibre(cepo) {2}".format(
|
|
longueur, diametre, calibre))
|
|
except IndexError:
|
|
self.lbl_module.set_text("")
|
|
|
|
# Validation de l'ajout d'un nouveau cigare
|
|
def on_btadd_clicked(self, widget):
|
|
del widget
|
|
index = self.cbmarques.get_active()
|
|
model = self.cbmarques.get_model()
|
|
itemmarque = model[index]
|
|
# item[0] = id à insérer dans champ marque de la table cigares
|
|
# print (str(itemmarque[0]), str(itemmarque[1]))
|
|
|
|
index = self.cbmodules.get_active()
|
|
model = self.cbmodules.get_model()
|
|
itemmodel = model[index]
|
|
# item[0] = id à insérer dans champ marque de la table cigares
|
|
# print (str(itemmodel[0]), str(itemmodel[1]))
|
|
# Il faudra faire une requete sur cigares pour obtenir le dernier id
|
|
# et faire + 1 : Select max(id) from cigares
|
|
# table stocks : id_cigare = max id + 1
|
|
# print (str(self.entry_nom_cigare.get_text()))
|
|
|
|
cursor = db.cursor()
|
|
cursor.execute('Select max(id) from cigares')
|
|
try:
|
|
maxid = cursor.fetchone()[0] + 1
|
|
except TypeError:
|
|
maxid = 1
|
|
cursor.execute("insert into stocks (id_cigare, quantite) " +
|
|
"values ('{0}','{1}')".format(int(maxid), 0))
|
|
cursor.execute("insert into cigares (designation, marque, module) " +
|
|
"values ('{0}','{1}','{2}')".format(
|
|
str(self.entry_nom_cigare.get_text()),
|
|
int(itemmarque[0]), int(itemmodel[0])))
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
# On recharge le treeview
|
|
self.loadtreeview()
|
|
|
|
# Affichage de la fenêtre de gestion des achats
|
|
def on_gtk_achats_activate(self, widget):
|
|
del widget
|
|
# On remet les selections à zero
|
|
self.scale_qte_achats.set_value(1)
|
|
self.scale_typeboite.set_value(25)
|
|
self.entry_prix_achats.set_text("")
|
|
self.entry_code_boite.set_text("")
|
|
self.entry_prix_boite.set_text("")
|
|
self.notebook_achats.set_current_page(0)
|
|
# On vide le listestore_liste_cigares (2è recherche)
|
|
if len(self.tablechoixcig) != 0:
|
|
self.tablechoixcig.clear()
|
|
|
|
cursor = db.cursor()
|
|
cursor.execute('Select c.id, c.designation from cigares c ' +
|
|
'order by c.designation asc')
|
|
rs = cursor.fetchall()
|
|
for item in rs:
|
|
iter = self.tablechoixcig.append()
|
|
self.tablechoixcig.set(iter, 0, item[0], 1, item[1])
|
|
cursor.close()
|
|
# On affiche la fenêtre de gestion des achats
|
|
self.dialog_achats.run()
|
|
|
|
# Cancel sur l'ajout d'un achat
|
|
def on_bt_cancel_achats_clicked(self, widget):
|
|
del widget
|
|
self.dialog_achats.hide()
|
|
|
|
# Validation des achats
|
|
def on_bt_valid_achats_clicked(self, widget):
|
|
del widget
|
|
# On récupère le cigare choisi dans la liste déroulante
|
|
# [0] id_cigare ~ [1] libelle
|
|
index = self.cb_liste_cigares_achats.get_active()
|
|
itemchoixcig = self.tablechoixcig[index]
|
|
|
|
id_cigare = itemchoixcig[0]
|
|
|
|
# On vérifie les saisies
|
|
if index == -1: # Aucun cigare choisi
|
|
self.msgdialog.format_secondary_text(
|
|
"Vous devez d'abord choisir un cigare dans la liste")
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
return
|
|
# Mauvais prix unité
|
|
if self.notebook_achats.get_current_page() == 0 \
|
|
and not is_number(self.entry_prix_achats.get_text()):
|
|
self.msgdialog.format_secondary_text(
|
|
"Saisie erronée pour le prix du cigare.")
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
return
|
|
# Mauvais prix boite
|
|
if self.notebook_achats.get_current_page() == 1 \
|
|
and not is_number(self.entry_prix_boite.get_text()):
|
|
self.msgdialog.format_secondary_text(
|
|
"Saisie erronée pour le prix de la boite.")
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
return
|
|
if self.notebook_achats.get_current_page() == 0: # Choix à l'unité
|
|
qte_achat = int(self.scale_qte_achats.get_value())
|
|
prix_achat = self.entry_prix_achats.get_text().replace(",", ".")
|
|
prix_achat = '%.2f' % float(prix_achat)
|
|
code_boite = ""
|
|
else: # = 1 choix par boites
|
|
qte_achat = int(self.scale_typeboite.get_value())
|
|
prix_achat = float(self.entry_prix_boite.get_text()) / qte_achat
|
|
prix_achat = '%.2f' % prix_achat
|
|
code_boite = self.entry_code_boite.get_text()
|
|
print(qte_achat)
|
|
print(prix_achat)
|
|
print(code_boite)
|
|
|
|
# On formate la date (ajout d'un 0 si besoin)
|
|
j = str(self.calendar_date_achats.get_date()[2])
|
|
if len(str(self.calendar_date_achats.get_date()[1] + 1)) == 1:
|
|
m = "0" + str(self.calendar_date_achats.get_date()[1] + 1)
|
|
else:
|
|
m = str(self.calendar_date_achats.get_date()[1] + 1)
|
|
a = self.calendar_date_achats.get_date()[0]
|
|
|
|
date_achat = "{0}/{1}/{2}".format(j, m, a)
|
|
|
|
# Insert des achats en base de données
|
|
cursor = db.cursor()
|
|
cursor.execute(
|
|
"insert into achats (id_cigare, date, prix, quantite, " +
|
|
"code_boite) values ('{0}','{1}','{2}','{3}', '{4}')".format(
|
|
id_cigare,
|
|
date_achat,
|
|
prix_achat,
|
|
qte_achat,
|
|
code_boite))
|
|
# ajout dans le stock
|
|
cursor.execute("update stocks set quantite = " +
|
|
"quantite + {1} where id = {0}"
|
|
.format(id_cigare, qte_achat))
|
|
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
# Afficher un message d'infos pour confirmer
|
|
self.msgdialog.format_secondary_text(
|
|
"Achats ajoutés avec succés " +
|
|
"pour {0}".format(itemchoixcig[1]))
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
|
|
# On recharge la liste des cigares
|
|
self.loadtreeview()
|
|
|
|
self.dialog_achats.hide()
|
|
|
|
# Affiche du prix d'un cigare pour les achats par boite
|
|
def on_scale_typeboite_change_value(self, scale, enum, new_value):
|
|
if is_number(self.entry_prix_boite.get_text()):
|
|
prix_unite = float(self.entry_prix_boite.get_text()) / \
|
|
int(self.scale_typeboite.get_value())
|
|
prix_unite = '%.2f' % prix_unite
|
|
# on met à jour le label
|
|
self.lbl_prix_unite_boite.set_text(
|
|
" Soit {0}€ le cigare".format(prix_unite))
|
|
|
|
def on_entry_prix_boite_changed(self, widget):
|
|
del widget
|
|
self.on_scale_typeboite_change_value(None, None, None)
|
|
|
|
# Affichage de la fenêtre d'ajout d'une marque
|
|
def on_gtk_add_marque_activate(self, menuitem, data=None):
|
|
del menuitem, data
|
|
self.dialog_add_marque.run()
|
|
self.dialog_add_marque.hide()
|
|
|
|
# Validation de l'ajout d'une marque
|
|
def on_bt_add_marque_clicked(self, widget):
|
|
del widget
|
|
print("todo")
|
|
e_marque = self.entry_marque.get_text().title()
|
|
e_provenance = self.entry_provenance.get_text().title()
|
|
|
|
# Insert de la nouvelle marque en base de données
|
|
cursor = db.cursor()
|
|
cursor.execute("insert into marques (libelle, provenance) " +
|
|
"values ('{0}','{1}')".format(e_marque, e_provenance))
|
|
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
# Afficher un message d'infos pour confirmer
|
|
self.msgdialog.format_secondary_text(
|
|
"Marque {0} ajoutée.".format(e_marque))
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
|
|
# Affichage de la fenêtre de gestion des stocks
|
|
def on_gtk_stocks_activate(self, menuitem, data=None):
|
|
del menuitem, data
|
|
# On commence par vider le listestore_liste_cigares (2è recherche)
|
|
if len(self.tablechoixcig) != 0:
|
|
self.tablechoixcig.clear()
|
|
|
|
cursor = db.cursor()
|
|
|
|
sql = ('Select c.id, c.designation '
|
|
'from cigares c order by c.designation asc')
|
|
|
|
cursor.execute(sql)
|
|
|
|
rs = cursor.fetchall()
|
|
for item in rs:
|
|
iter = self.tablechoixcig.append()
|
|
self.tablechoixcig.set(iter, 0, item[0], 1, item[1])
|
|
cursor.close()
|
|
|
|
self.dialog_stocks.run()
|
|
self.dialog_stocks.hide()
|
|
|
|
# Validation des stocks
|
|
def on_btstockvalid_clicked(self, value):
|
|
item = self.cbchoixcigqte.get_active()
|
|
model = self.cbchoixcigqte.get_model()
|
|
idc = model[item]
|
|
|
|
cursor = db.cursor()
|
|
cursor.execute(
|
|
"Update stocks set quantite='{0}' where id_cigare='{1}'".format(
|
|
int(self.scaleqte.get_value()), idc[0]))
|
|
|
|
db.commit()
|
|
cursor.close()
|
|
# affichage message maj ok pour qte
|
|
self.msgdialog.format_secondary_text(
|
|
"Quantité mise à jour pour " +
|
|
"{0}.\nVous avez maintenant {1} exemplaire(s)"
|
|
.format(idc[1], int(self.scaleqte.get_value())))
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
# On recharge la treeview principale
|
|
self.loadtreeview()
|
|
|
|
# Affichage de la fenêtre de dégustation
|
|
def on_gtk_degustation_activate(self, menuitem, data=None):
|
|
del menuitem, data
|
|
if len(self.tablechoixcigare) != 0:
|
|
self.tablechoixcigare.clear()
|
|
|
|
cursor = db.cursor()
|
|
cursor.execute(
|
|
'Select c.id, c.designation from cigares c where c.id not in ' +
|
|
'(select s.id_cigare from stocks s where s.quantite = 0) ' +
|
|
'order by c.designation asc')
|
|
rs = cursor.fetchall()
|
|
for item in rs:
|
|
iter = self.tablechoixcigare.append()
|
|
self.tablechoixcigare.set(iter, 0, item[0], 1, item[1])
|
|
|
|
cursor.close()
|
|
self.window_degustation.run()
|
|
self.window_degustation.hide()
|
|
|
|
# Validation d'une dégusation
|
|
def on_btvalid_clicked(self, widget):
|
|
del widget
|
|
# On formate la date (ajout d'un 0 si besoin)
|
|
j = str(self.degustationdate.get_date()[2])
|
|
if len(str(self.degustationdate.get_date()[1] + 1)) == 1:
|
|
m = "0" + str(self.degustationdate.get_date()[1] + 1)
|
|
else:
|
|
m = str(self.degustationdate.get_date()[1] + 1)
|
|
a = self.degustationdate.get_date()[0]
|
|
|
|
# Formatage de la date du calendar au format %d/%m/%Y
|
|
DateDegustation = "{0}/{1}/{2}".format(j, m, a)
|
|
|
|
textbuffer = self.txtview_commentaire.get_buffer()
|
|
start = textbuffer.get_start_iter()
|
|
end = textbuffer.get_end_iter()
|
|
# On récupère le commentaire et on échape les quote pour l'insert SQL
|
|
textlines = textbuffer.get_text(
|
|
start, end, include_hidden_chars=False).replace("'", "''")
|
|
|
|
index = self.cbchoixcigare.get_active()
|
|
itemchoixcig = self.tablechoixcigare[index]
|
|
|
|
note = int(self.scaleq1.get_value()) + int(self.scaleq2.get_value()) \
|
|
+ int(self.scaleq3.get_value()) + int(self.scaleq4.get_value()) \
|
|
+ int(self.scaleq5.get_value()) + int(self.scaleq6.get_value()) \
|
|
+ int(self.scaleq7.get_value()) + int(self.scaleq8.get_value()) \
|
|
+ int(self.scaleq9.get_value()) + int(self.scaleq10.get_value()) \
|
|
+ int(self.scaleq11.get_value()) + int(self.scaleq12.get_value()) \
|
|
+ int(self.scaleq13.get_value())
|
|
|
|
cursor = db.cursor()
|
|
req = ("insert into degustation (id_cigare,date,commentaires," +
|
|
"q1,q2,q3,q4,q5,q6,q7,q8,q9,q10,q11,q12,q13,note) " +
|
|
"values ('{0}','{1}','{2}','{3}','{4}','{5}','{6}'" +
|
|
",'{7}','{8}','{9}','{10}','{11}','{12}','{13}'," +
|
|
"'{14}','{15}','{16}')")
|
|
cursor.execute(req.format(itemchoixcig[0], DateDegustation, textlines,
|
|
int(self.scaleq1.get_value()),
|
|
int(self.scaleq2.get_value()),
|
|
int(self.scaleq3.get_value()),
|
|
int(self.scaleq4.get_value()),
|
|
int(self.scaleq5.get_value()),
|
|
int(self.scaleq6.get_value()),
|
|
int(self.scaleq7.get_value()),
|
|
int(self.scaleq8.get_value()),
|
|
int(self.scaleq9.get_value()),
|
|
int(self.scaleq10.get_value()),
|
|
int(self.scaleq11.get_value()),
|
|
int(self.scaleq12.get_value()),
|
|
int(self.scaleq13.get_value()), note))
|
|
# - 1 sur le stock
|
|
cursor.execute("update stocks set quantite = quantite - 1 " +
|
|
"where id = {0}".format(itemchoixcig[0]))
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
# TODO(jlaunay) ajouter une checkbox sur le formulaire
|
|
# pour choisir si retirer 1 du stock ou non
|
|
self.msgdialog.format_secondary_text(
|
|
"Note de dégustation ajoutée avec succès pour : " +
|
|
"{0}.\n1 cigare retiré du stock.".format(itemchoixcig[1]))
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
|
|
# On recharge la treeview
|
|
self.loadtreeview()
|
|
|
|
# Affichage de la fenêtre des cigares à tester
|
|
def on_gtk_wish_activate(self, menuitem, data=None):
|
|
del menuitem, data
|
|
# On commence par vider le listestore_wishes (2è recherche)
|
|
if len(self.liststore_wishes) != 0:
|
|
self.liststore_wishes.clear()
|
|
|
|
cursor = db.cursor()
|
|
cursor.execute(
|
|
'select nom, provenance,raison,source,id from tester order by id')
|
|
rs = cursor.fetchall()
|
|
for item in rs:
|
|
iter = self.liststore_wishes.append()
|
|
self.liststore_wishes.set(iter, 0, item[0], 1, item[1],
|
|
2, item[2], 3, item[3], 4, item[4])
|
|
cursor.close()
|
|
|
|
self.window_wishes.resize(self.window.get_size()[0],
|
|
self.window.get_size()[1])
|
|
|
|
self.window_wishes.show()
|
|
|
|
# Quitter la fenêtre des cigares à tester
|
|
def on_window_wishes_delete_event(self, widget, data=None):
|
|
del widget, data
|
|
self.window_wishes.hide()
|
|
return True
|
|
|
|
# Affichage de l'assistant d'aide pour la documentation
|
|
def on_gtk_how_to_activate(self, widget):
|
|
del widget
|
|
self.doc.show()
|
|
# self.doc.hide()
|
|
|
|
def on_lbl_intro_activate_link(self, widget):
|
|
del widget
|
|
self.doc.set_forward_page_func(page_func=None, data=None)
|
|
|
|
# Quitter la fenêtre d'assistant
|
|
def on_assistant_delete_event(self, widget, data=None):
|
|
del widget, data
|
|
self.doc.hide()
|
|
return True
|
|
|
|
# Affichage de la fenêtre d'ajout d'un cigare à tester
|
|
def on_bt_add_wish_clicked(self, widget):
|
|
del widget
|
|
self.dialog_add_wish.run()
|
|
self.dialog_add_wish.hide()
|
|
|
|
# Valider l'ajout d'un cigare à tester
|
|
def on_bt_valid_add_wish_clicked(self, widget):
|
|
del widget
|
|
textbuffer = self.txtview_test_raison.get_buffer()
|
|
start = textbuffer.get_start_iter()
|
|
end = textbuffer.get_end_iter()
|
|
# On récupère le commentaire et on échape les quote pour l'insert SQL
|
|
textlines = textbuffer.get_text(
|
|
start, end, include_hidden_chars=False).replace("'", "''")
|
|
|
|
# On ajoute en base de données
|
|
cursor = db.cursor()
|
|
sql = ("insert into tester (nom, provenance, source, raison) " +
|
|
"values ('{0}','{1}','{2}','{3}')"
|
|
.format(self.entry_test_nom.get_text(),
|
|
self.entry_test_origine.get_text(),
|
|
self.entry_test_source.get_text(), textlines))
|
|
cursor.execute(sql)
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
# On recharge la treeview des cigares à tester
|
|
self.on_gtk_wish_activate(self)
|
|
|
|
# Clic droit sur un cigare à tester dans la treeview
|
|
def on_treeview_wishes_button_press_event(self, treeview, event):
|
|
x = int(event.x)
|
|
y = int(event.y)
|
|
if event.button == 3: # Clic droit
|
|
pthinfo = self.treeview_wishes.get_path_at_pos(x, y)
|
|
if pthinfo is not None:
|
|
path, col, cellx, celly = pthinfo
|
|
self.treeview_wishes.grab_focus()
|
|
self.treeview_wishes.set_cursor(path, col, 0)
|
|
# On affiche le popup
|
|
self.popup_test.show_all()
|
|
self.popup_test.popup(None, None, None, None,
|
|
event.button, event.time)
|
|
|
|
# Ouvrir l'url dans le navigateur au clic droit sur une ligne de la
|
|
# treeview des cigares à tester
|
|
def on_gtk_copier_test_activate(self, widget):
|
|
del widget
|
|
try:
|
|
import webbrowser
|
|
from urllib.parse import urlparse
|
|
except ImportError:
|
|
print('webbrowser not available')
|
|
return False
|
|
|
|
selection = self.treeview_wishes.get_selection()
|
|
(model, iter) = selection.get_selected()
|
|
if iter is not None:
|
|
url = model.get_value(iter, 3)
|
|
if not urlparse(url).scheme:
|
|
url = "http://" + url
|
|
# On copie dans le presse papier
|
|
webbrowser.open(url)
|
|
|
|
# Pour supprimer au clic droit
|
|
def on_gtk_supprimer_activate(self, widget):
|
|
del widget
|
|
id_supp = self.treeview_selection[5]
|
|
cursor = db.cursor()
|
|
sql = "delete from cigares where id = {0}".format(id_supp)
|
|
cursor.execute(sql)
|
|
sql = "delete from achats where id_cigare = {0}".format(id_supp)
|
|
cursor.execute(sql)
|
|
sql = "delete from stocks where id_cigare = {0}".format(id_supp)
|
|
cursor.execute(sql)
|
|
sql = "delete from degustation where id_cigare = {0}".format(id_supp)
|
|
cursor.execute(sql)
|
|
sql = "delete from photos where id_cigare = {0}".format(id_supp)
|
|
cursor.execute(sql)
|
|
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
# On recharge la treeview
|
|
self.loadtreeview()
|
|
|
|
# Pour supprimer un cigare à tester au clic droit
|
|
def on_gtk_supprimer_test_activate(self, widget):
|
|
del widget
|
|
selection = self.treeview_wishes.get_selection()
|
|
(model, iter) = selection.get_selected()
|
|
if iter is not None:
|
|
cursor = db.cursor()
|
|
sql = ("delete from tester where nom = '{0}' and provenance = " +
|
|
"'{1}' and raison = '{2}' and source = '{3}'"
|
|
.format(model.get_value(iter, 0),
|
|
model.get_value(iter, 1),
|
|
model.get_value(iter, 2),
|
|
model.get_value(iter, 3)))
|
|
cursor.execute(sql)
|
|
|
|
db.commit()
|
|
cursor.close()
|
|
|
|
# Afficher un message d'infos pour alerter que c'est bien supprimé
|
|
self.msgdialog.format_secondary_text(
|
|
"Cigare à tester correctement supprimé")
|
|
self.msgdialog.run()
|
|
self.msgdialog.hide()
|
|
|
|
# On recharge la treeview
|
|
self.on_gtk_wish_activate(self)
|
|
|
|
# Permet le filtrage de la treeview de cigares
|
|
def filter_func(self, model, iterr, data=None):
|
|
query = self.entry_search.get_buffer().get_text()
|
|
value = self.table.get_value(iterr, 0)
|
|
|
|
try:
|
|
if query == "":
|
|
return True
|
|
elif query in value.lower():
|
|
return True
|
|
except AttributeError:
|
|
pass
|
|
return False
|
|
# Filtrer au changement sur entry_search
|
|
|
|
def on_entry_search_changed(self, data=None):
|
|
self.tree_filter.refilter()
|
|
|
|
def __init__(self):
|
|
self.gladefile = "ui/ui.glade"
|
|
self.builder = gtk.Builder()
|
|
self.builder.add_from_file(self.gladefile)
|
|
self.builder.connect_signals(self)
|
|
self.window = self.builder.get_object("window_main")
|
|
self.aboutdialog = self.builder.get_object("aboutdialog")
|
|
self.statusbar = self.builder.get_object("window_main_statusbar")
|
|
self.context_id = self.statusbar.get_context_id("exemple")
|
|
self.table = self.builder.get_object("liststore_treeview_cigares")
|
|
self.dialog_add_cigare = self.builder.get_object("dialog_add_cigare")
|
|
self.dialog_add_marque = self.builder.get_object("dialog_add_marque")
|
|
self.dialog_add_module = self.builder.get_object("dialog_add_module")
|
|
|
|
# Modification/Suppression d'une note de dégustation
|
|
self.window_modif_degustation = self.builder.get_object(
|
|
"window_modif_degustation")
|
|
self.lstore_dmodif_degust = self.builder.get_object(
|
|
"liststore_date_modif_degustation")
|
|
self.cbchoix_date_modif_degustation = self.builder.get_object(
|
|
"cbchoix_date_modif_degustation")
|
|
self.scaleq1m = self.builder.get_object("scaleq1m")
|
|
self.scaleq2m = self.builder.get_object("scaleq2m")
|
|
self.scaleq3m = self.builder.get_object("scaleq3m")
|
|
self.scaleq4m = self.builder.get_object("scaleq4m")
|
|
self.scaleq5m = self.builder.get_object("scaleq5m")
|
|
self.scaleq6m = self.builder.get_object("scaleq6m")
|
|
self.scaleq7m = self.builder.get_object("scaleq7m")
|
|
self.scaleq8m = self.builder.get_object("scaleq8m")
|
|
self.scaleq9m = self.builder.get_object("scaleq9m")
|
|
self.scaleq10m = self.builder.get_object("scaleq10m")
|
|
self.scaleq11m = self.builder.get_object("scaleq11m")
|
|
self.scaleq12m = self.builder.get_object("scaleq12m")
|
|
self.scaleq13m = self.builder.get_object("scaleq13m")
|
|
self.lbl_note_totale = self.builder.get_object("lbl_note_totale")
|
|
self.txtview_commentaire_modif = self.builder.get_object(
|
|
"txtview_commentaire_modif")
|
|
|
|
self.cbmarques = self.builder.get_object("cbmarques")
|
|
self.cbchoixcigare = self.builder.get_object("cbchoixcigare")
|
|
self.tablechoixcigare = self.builder.get_object(
|
|
"liststore_cigares_a_noter")
|
|
self.scaleq1 = self.builder.get_object("scaleq1")
|
|
self.scaleq2 = self.builder.get_object("scaleq2")
|
|
self.scaleq3 = self.builder.get_object("scaleq3")
|
|
self.scaleq4 = self.builder.get_object("scaleq4")
|
|
self.scaleq5 = self.builder.get_object("scaleq5")
|
|
self.scaleq6 = self.builder.get_object("scaleq6")
|
|
self.scaleq7 = self.builder.get_object("scaleq7")
|
|
self.scaleq8 = self.builder.get_object("scaleq8")
|
|
self.scaleq9 = self.builder.get_object("scaleq9")
|
|
self.scaleq10 = self.builder.get_object("scaleq10")
|
|
self.scaleq11 = self.builder.get_object("scaleq11")
|
|
self.scaleq12 = self.builder.get_object("scaleq12")
|
|
self.scaleq13 = self.builder.get_object("scaleq13")
|
|
self.tablemarques = self.builder.get_object("liststore_marques")
|
|
self.cbmodules = self.builder.get_object("cbmodules")
|
|
self.tablemodules = self.builder.get_object("liststore_modules")
|
|
self.entry_nom_cigare = self.builder.get_object("entry_nom_cigare")
|
|
self.window_degustation = self.builder.get_object("window_degustation")
|
|
self.txtview_commentaire = self.builder.get_object(
|
|
"txtview_commentaire")
|
|
self.popup = self.builder.get_object("popup")
|
|
self.popup_test = self.builder.get_object("popup_test")
|
|
self.filechooser_image = self.builder.get_object("filechooser_image")
|
|
self.filechooser_bdd = self.builder.get_object("filechooser_bdd")
|
|
|
|
# Fenêtre d'ajout d'une marque
|
|
self.entry_marque = self.builder.get_object("entry_marque")
|
|
self.entry_provenance = self.builder.get_object("entry_provenance")
|
|
|
|
# Fenêtre des cigares à tester
|
|
self.txtview_test_raison = self.builder.get_object(
|
|
"txtview_test_raison")
|
|
self.entry_test_nom = self.builder.get_object("entry_test_nom")
|
|
self.entry_test_source = self.builder.get_object("entry_test_source")
|
|
self.entry_test_origine = self.builder.get_object("entry_test_origine")
|
|
|
|
self.lbl_marque = self.builder.get_object("lbl_marque")
|
|
self.lbl_module = self.builder.get_object("lbl_module")
|
|
|
|
self.msgdialog = self.builder.get_object("msgdialog")
|
|
|
|
self.tablechoixcig = self.builder.get_object(
|
|
"listestore_liste_cigares")
|
|
self.cbchoixcigqte = self.builder.get_object("cbchoixcigqte")
|
|
self.lblqte = self.builder.get_object("lblqte")
|
|
self.dialog_stocks = self.builder.get_object("dialog_stocks")
|
|
self.scaleqte = self.builder.get_object("scaleqte")
|
|
self.adjqte = self.builder.get_object("adjqte")
|
|
# Assistant pour la documentation de l'appli
|
|
self.doc = self.builder.get_object("assistant")
|
|
# Image pour afficher le preview dans la zone rétractable
|
|
self.image_preview = self.builder.get_object("image_preview")
|
|
# Date pour la dégustation
|
|
self.degustationdate = self.builder.get_object("calendar_degustation")
|
|
|
|
# Fenêtre de gestion des achats
|
|
self.dialog_achats = self.builder.get_object("dialog_achats")
|
|
# Combo pour la liste des cigares (achats) - utilise aussi
|
|
# listestore_choix_cigare_qte
|
|
self.cb_liste_cigares_achats = self.builder.get_object(
|
|
"cb_liste_cigares_achats")
|
|
# Scale pour la quantité achetée
|
|
self.scale_qte_achats = self.builder.get_object("scale_qte_achats")
|
|
# Entry pour le prix d'achat
|
|
self.entry_prix_achats = self.builder.get_object("entry_prix_achats")
|
|
# Notebook (tabs) pour les achats
|
|
self.notebook_achats = self.builder.get_object("notebook_achats")
|
|
self.scale_typeboite = self.builder.get_object("scale_typeboite")
|
|
self.entry_prix_boite = self.builder.get_object("entry_prix_boite")
|
|
self.entry_code_boite = self.builder.get_object("entry_code_boite")
|
|
self.lbl_prix_unite_boite = self.builder.get_object(
|
|
"lbl_prix_unite_boite")
|
|
|
|
# Calendar pour la date d'achat
|
|
self.calendar_date_achats = self.builder.get_object(
|
|
"calendar_date_achats")
|
|
# Fenêtre pour la liste des cigares à tester
|
|
self.window_wishes = self.builder.get_object("window_wishes")
|
|
# Fenêtre d'ajout d'un cigare à tester
|
|
self.dialog_add_wish = self.builder.get_object("dialog_add_wish")
|
|
# Treeview wishes
|
|
self.treeview_wishes = self.builder.get_object("treeview_wishes")
|
|
# Liststore associé à treeview wishes
|
|
self.liststore_wishes = self.builder.get_object("liststore_wishes")
|
|
|
|
# Ligne séléctionnée au changement sur la treeview
|
|
self.treeview_selection = []
|
|
# Chemin de l'image preview
|
|
self.imgpath = ""
|
|
self.window_last_h = self.window.get_size()[1]
|
|
self.window_last_w = self.window.get_size()[0]
|
|
|
|
# Recherche et filrage d'un cigare
|
|
self.entry_search = self.builder.get_object("entry_search")
|
|
|
|
# Filter la treeview des cigares
|
|
self.tree_filter = self.table.filter_new()
|
|
self.tree_filter.set_visible_func(self.filter_func)
|
|
self.filtered_model = gtk.TreeModelSort(model=self.tree_filter)
|
|
|
|
# Treeview de la liste des cigares
|
|
self.tree = self.builder.get_object("treeview_cigares")
|
|
|
|
# On défini le modèle pour la treeview qui permet le filtrage
|
|
self.tree.set_model(self.filtered_model)
|
|
|
|
# Constuire l'entête du treeview
|
|
self.editable_cell_nom = gtk.CellRendererText()
|
|
self.no_editable_cell = gtk.CellRendererText()
|
|
self.editable_cell_qte = gtk.CellRendererText()
|
|
self.invisible_cell = gtk.CellRendererText()
|
|
|
|
self.editable_cell_nom.connect('edited', self.cell_edited_callback, 0)
|
|
self.editable_cell_nom.set_property('editable', True)
|
|
|
|
self.col_nom = gtk.TreeViewColumn(
|
|
'Nom', self.editable_cell_nom, text=0, background=11)
|
|
self.col_nom.set_sort_column_id(0)
|
|
self.tree.append_column(self.col_nom)
|
|
|
|
# Recherche d'un cigare
|
|
self.tree.set_search_column(0)
|
|
self.tree.set_search_entry(self.entry_search)
|
|
|
|
self.no_editable_cell.set_property('editable', False)
|
|
|
|
self.col_marque = gtk.TreeViewColumn(
|
|
'Marque', self.no_editable_cell, text=1, background=11)
|
|
self.col_marque.set_sort_column_id(1)
|
|
self.tree.append_column(self.col_marque)
|
|
|
|
self.col_module = gtk.TreeViewColumn(
|
|
'Module', self.no_editable_cell, text=2, background=11)
|
|
self.col_module.set_sort_column_id(2)
|
|
self.tree.append_column(self.col_module)
|
|
|
|
self.col_provenance = gtk.TreeViewColumn(
|
|
'Terroir', self.no_editable_cell, text=3, background=11)
|
|
self.col_provenance.set_sort_column_id(3)
|
|
self.tree.append_column(self.col_provenance)
|
|
|
|
self.editable_cell_qte.connect('edited', self.cell_edited_callback, 4)
|
|
self.editable_cell_qte.set_property('editable', True)
|
|
self.col_qte = gtk.TreeViewColumn(
|
|
'Quantité', self.editable_cell_qte, text=4, background=11)
|
|
self.col_qte.set_sort_column_id(4)
|
|
self.tree.append_column(self.col_qte)
|
|
|
|
# colonne invisible pour l'id_cigare
|
|
self.invisible_cell.set_property('visible', False)
|
|
self.col_id = gtk.TreeViewColumn(
|
|
'id', self.invisible_cell, text=5, background=11)
|
|
self.col_id.set_sort_column_id(5)
|
|
|
|
self.col_best_note = gtk.TreeViewColumn(
|
|
'Meilleure note', self.no_editable_cell, text=6, background=11)
|
|
self.col_best_note.set_sort_column_id(6)
|
|
self.tree.append_column(self.col_best_note)
|
|
|
|
self.col_mid_note = gtk.TreeViewColumn(
|
|
'Note moyenne', self.no_editable_cell, text=7, background=12)
|
|
self.col_mid_note.set_sort_column_id(7)
|
|
self.tree.append_column(self.col_mid_note)
|
|
|
|
self.col_prix = gtk.TreeViewColumn(
|
|
'Prix', self.no_editable_cell, text=8, background=11)
|
|
# On utilise l'id de la col_tri (id = 10) qui contient le prix en float
|
|
# pour le tri
|
|
self.col_prix.set_sort_column_id(10)
|
|
self.tree.append_column(self.col_prix)
|
|
|
|
# colonne invisible pour le commentaire de dégustation
|
|
self.col_comment = gtk.TreeViewColumn(
|
|
'commentaire', self.invisible_cell, text=9)
|
|
self.col_comment.set_sort_column_id(9)
|
|
# On défini cette colonne comme colonne d'infobulle
|
|
self.tree.set_tooltip_column(9)
|
|
|
|
# colonne invisible pour le tri des prix
|
|
self.col_tri = gtk.TreeViewColumn('tri', self.invisible_cell, text=10)
|
|
|
|
# colonne invisible pour la couleur d'une ligne
|
|
self.col_couleur = gtk.TreeViewColumn(
|
|
'couleur', self.invisible_cell, text=11)
|
|
|
|
# colonne invisible pour la couleur du top 5
|
|
self.col_couleurtop5 = gtk.TreeViewColumn(
|
|
'couleurtop5', self.invisible_cell, text=12)
|
|
|
|
# Utiliser gtkspellcheck dans la txtview des commentaires
|
|
# http://koehlma.github.io/projects/pygtkspellcheck.html
|
|
try:
|
|
from gtkspellcheck import SpellChecker
|
|
SpellChecker(self.txtview_commentaire,
|
|
locale.getdefaultlocale()[0])
|
|
SpellChecker(self.txtview_commentaire_modif,
|
|
locale.getdefaultlocale()[0])
|
|
SpellChecker(self.txtview_test_raison,
|
|
locale.getdefaultlocale()[0])
|
|
except Exception:
|
|
print("gtkspellcheck n'est pas installé " +
|
|
"http://koehlma.github.io/projects/" +
|
|
"pygtkspellcheck.html")
|
|
pass
|
|
|
|
self.window.show_all()
|
|
# On charge la liste des cigares
|
|
self.loadtreeview()
|
|
|
|
|
|
# Vérifier qu'il s'agit d'un nombre
|
|
def is_number(s):
|
|
try:
|
|
float(s)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
|
|
if __name__ == "__main__":
|
|
dbconf = mescigaresdb()
|
|
madb = dbconf.checkconfig()
|
|
db = sqlite3.connect(madb)
|
|
main = MesCigares()
|
|
main.window.set_title(
|
|
"MesCigares - base : {0}".format(ntpath.basename(madb))
|
|
.replace('.db', ''))
|
|
gtk.main()
|