mescigares/app_gestion/MesCigares.py

1769 lines
72 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Application de gestion de cave à cigares
WTFPL @ Jérôme LAUNAY
"""
import os
import sys
import locale
import ntpath
import sqlite3
import configparser
import gi
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)
__BDD__ = "bdd/main.db"
__CONFDIR__ = os.path.dirname(os.path.realpath("MesCigares.py"))
__CONFIGFILE__ = "conf/mescigares.conf"
"""
Class MesCigares
"""
class MesCigares(object):
@classmethod
def on_window_main_destroy(cls, widget, data=None):
db.close()
gtk.main_quit()
@classmethod
def on_gtk_quit_activate(cls, menuitem, data=None):
db.close()
gtk.main_quit()
def on_gtk_about_activate(self, menuitem, data=None):
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):
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 = str('%.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 = str('%.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):
# 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_treeview_cigares_row_activated(self, 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):
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_filechooser_bdd_file_activated(self, 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):
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):
self.filechooser_image.run()
self.filechooser_image.hide()
# Modifier/Supprimer une note de degustation
def on_modsupp_degustation_activate(self, 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_date_modif_degustation_changed(self, 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):
# 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):
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):
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):
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):
# 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):
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):
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):
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):
# 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):
self.dialog_achats.hide()
# Validation des achats
def on_bt_valid_achats_clicked(self, 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):
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):
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):
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):
# 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):
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):
# 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):
# 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):
self.window_wishes.hide()
return True
# Affichage de l'assistant d'aide pour la documentation
def on_gtk_how_to_activate(self, widget):
self.doc.show()
# self.doc.hide()
def on_lbl_intro_activate_link(self, 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):
self.doc.hide()
return True
# Affichage de la fenêtre d'ajout d'un cigare à tester
def on_bt_add_wish_clicked(self, 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):
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):
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):
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):
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
def createdb(new_bdd):
db = sqlite3.connect(new_bdd)
cursor = db.cursor()
sql = ('create table achats(id INTEGER PRIMARY KEY, id_cigare '
'INTEGER, date TEXT, code_boite TEXT, prix FLOAT, '
'quantite INTEGER)')
cursor.execute(sql)
sql = ('create table cigares(id INTEGER PRIMARY KEY, designation '
'TEXT, marque INTEGER, module INTEGER)')
cursor.execute(sql)
sql = ('create table degustation'
'(id INTEGER PRIMARY KEY, id_cigare INTEGER, date TEXT, '
'note NUMERIC, commentaires TEXT, q1 INTEGER, q2 INTEGER, '
'q3 INTEGER, q4 INTEGER, q5 INTEGER, q6 INTEGER, q7 INTEGER, '
'q8 INTEGER, q9 INTEGER, q10 INTEGER, q11 INTEGER, q12 INTEGER, '
'q13 INTEGER)')
cursor.execute(sql)
sql = ('create table marques'
'(id INTEGER PRIMARY KEY, libelle TEXT, provenance TEXT)')
cursor.execute(sql)
sql = ('create table modules'
'(id INTEGER PRIMARY KEY, module TEXT, calibre TEXT, '
'diametre TEXT, longueur TEXT)')
cursor.execute(sql)
sql = ('create table photos'
'(id INTEGER PRIMARY KEY, id_cigare INTEGER, photo TEXT)')
cursor.execute(sql)
sql = ('create table stocks'
'(id INTEGER PRIMARY KEY, id_cigare INTEGER, quantite INTEGER)')
cursor.execute(sql)
sql = ('create table tester'
'(id INTEGER PRIMARY KEY, nom TEXT, provenance TEXT, '
'raison TEXT, source TEXT)')
cursor.execute(sql)
# INSERT à mettre dans un fichier à part
sql = "INSERT INTO modules VALUES(1, 'Laguito N °3', '26', '10.32', '115')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(2, 'Carolina', '26', '10.32', '121')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(3, 'Panetela Larga', '28', "
"'11.11', '175')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(4, 'Chico', '29', '11.51', '106')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(5, 'Entreacto', '30', '11.91', '100')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(6, 'Palmita', '32', '12.70', '152')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(7, 'Delicioso', '33', '13.10', '159')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(8, 'Palma', '33', '13.10', '170')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(9, 'Ninfa', '33', '13.10', '178')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(10, 'Panetela', '34', '13.49', '117')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(11, 'Placera', '34', '13.49', '125')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(12, 'Epicure', '35', '13.89', '110')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(13, 'Sport', '35', '13.89', '117')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(14, 'Conchita', '35', '13.89', '127')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(15, 'Carlota', '35', '13.89', '143')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(16, 'Cadete', '36', '14.29', '115')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(17, 'Seoane', '36', '14.29', '125')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(18, 'Veguerito', '36', '14.29', '127')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(19, 'Delicado Extra', "
"'36', '14.29', '185')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(20, 'Trabuco', '38', '15.08', '110')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(21, 'Laguito N ° 2', '38', "
"'15.08', '152')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(22, 'Parejo', '38', '15.08', '166')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(23, 'Delicado', '38', '15.08', '192')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(24, 'Laguito N ° 1', '38', "
"'15.08', '192')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(25, 'Belvedere', '39', '15.48', '125')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(26, 'Perla', '40', '15.87', '102')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(27, 'Franciscano', '40', '15.87', '116')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(28, 'Coronita', '40', '15.87', '117')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(29, 'Standard', '40', '15.87', '123')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(30, 'Londres', '40', '15.87', '126')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(31, 'Petit Cetro', '40', '15.87', '129')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(32, 'Almuerzo', '40', '15.87', '130')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(33, 'Crema', '40', '15.87', '140')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(34, 'Laguito No.1', '40', '15.87', "
"'192')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(35, 'Minuto', '42', '16.67', '110')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(36, 'Mareva', '42', '16.67', '129')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(37, 'Petit Corona', '42', '16.67', "
"'127')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(38, 'Eminente', '42', '16.67', '132')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(39, 'Nacional', '42', '15.87', '134')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(40, 'Cosaco', '42', '16.67', '135')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(41, 'Corona', '42', '16.67', '140')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(42, 'Corona Grande', '42', "
"'16.67', '155')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(43, 'Cervante', '42', '16.67', '165')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(44, 'Conserva', '43', '17.07', '145')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(45, 'Cazadore', '43', '17.07', '162')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(46, 'Dalia', '43', '17.07', '170')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(47, 'Francisco', '44', '17.46', '143')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(48, 'Corona Gorda', '46', "
"'18.26', '143')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(49, 'Taco', '47', '18.65', '158')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(50, 'Julieta / Churchill', "
"'47', '18.65', '178')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(51, 'Gran Corona', '47', '18.65', '235')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(52, 'Hermoso N ° 4', '49', "
"'19.05', '127')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(53, 'Paco', '49', '19.45', '180')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(54, 'Double corona', '49', "
"'19.45', '194')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(55, 'Robusto', '50', '19.84', '127')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(56, 'Gordito', '50', '19.84', '141')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(57, 'Campana', '52', '20.64', '140')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(58, 'Panetelas Extra', '37', "
"'13.49', '127')")
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(59, 'Oficios (Corona)', '43', "
"'17.07', '135')")
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(60, 'Majestic (Petit Corona)', "
"'39', '15.87', '140')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(61, 'Figurados', '60', '20+', '150+')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(62, 'Torpedo', '52', '20.8', '125')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(63, 'short robusto', '60', '24', '102')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(64, 'Mini Panetella', '20', '8', '125')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(65, 'Indéfini', '0', '0', '0')"
cursor.execute(sql)
sql = ("INSERT INTO modules VALUES(66, 'Très petit corona', '40', "
"'15.88', '102')")
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(67, 'Toro', '52', '20.8', '152')"
cursor.execute(sql)
sql = "INSERT INTO modules VALUES(68, 'Prominente', '49', '19.45', '194')"
cursor.execute(sql)
# MARQUES
sql = "INSERT INTO marques VALUES(1, 'Cohiba', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(2, 'Trinidad', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(3, 'Vegas Robaina', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(4, 'Montecristo', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(5, 'Cuaba', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(6, 'Romeo y Julieta', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(7, 'Partagas', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(8, 'San Cristobal de la Habana', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(9, 'Punch', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(10, 'Hoyo de Monterrey', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(11, 'Bolivar', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(12, 'La Gloria Cubana', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(13, 'H.Upmann', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(14, 'Fonseca', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(15, 'La Flor de Cano', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(16, 'Troya', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(17, 'Quintero', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(18, 'Los Statos de Luxe', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(19, 'Caney', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(20, 'Belinda', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(21, 'Cabanas', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(22, 'La Corona', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(23, 'José L.Piedra', 'cuba')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(24, 'Cumpay', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(25, 'CAO', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(26, 'Dunhill', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(27, 'Nicarao', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(28, 'Oliva', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(29, 'Padron', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(30, 'Flor de Copan', 'Honduras')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(31, 'Flor de Selva', 'Honduras')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(32, 'J. Cortès', 'Honduras')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(33, 'Zino', 'Honduras')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(34, 'Villa Zamorano', 'Honduras')"
cursor.execute(sql)
sql = ("INSERT INTO marques VALUES(35, "
"'Flor de Rafaël Gonzáles Márquez', 'cuba')")
cursor.execute(sql)
sql = ("INSERT INTO marques VALUES(36, 'Macanudo', "
"'République Dominicaine')")
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(37, 'Pléiades', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(38, 'Toscano', 'Italie')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(39, 'Davidoff', 'cuba')"
cursor.execute(sql)
sql = ("INSERT INTO marques VALUES(40, 'Avo Uvezian', "
"'République Dominicaine')")
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(41, 'Don Pepin Garcia', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(42, 'O Line', 'Nicaragua')"
cursor.execute(sql)
sql = "INSERT INTO marques VALUES(43, 'Plasencia', 'Nicaragua')"
cursor.execute(sql)
db.commit()
def checkconfig():
"""Récupérer la configuration ou la créer"""
# Fichier de configuration
# configfile = os.path.expanduser("~/.config/cltwit.conf")
# On ouvre le fichier de conf
config = configparser.RawConfigParser()
try:
config.read(__CONFIGFILE__)
if config.has_option('MesCigares', 'bdd'):
bdd = config.get('MesCigares', 'bdd')
except Exception:
pass
if not os.path.exists(bdd):
createdb(bdd)
# Si aucune conf
if not config.has_option('MesCigares', 'bdd'):
bdd = __CONFDIR__ + "/bdd/main.db"
# écrire le fichier de conf avec les informations par defaut
try:
cfgfile = open(__CONFIGFILE__, 'w')
if not config.has_section('MesCigares'):
config.add_section('MesCigares')
config.set('MesCigares', 'bdd', bdd)
config.write(cfgfile)
except IOError:
pass
finally:
cfgfile.close
createdb(bdd)
return bdd
def setconfig(new_bdd):
"""Mettre à jour la configuration"""
config = configparser.RawConfigParser()
try:
cfgfile = open(__CONFIGFILE__, 'w')
if not config.has_section('MesCigares'):
config.add_section('MesCigares')
config.set('MesCigares', 'bdd', new_bdd)
config.write(cfgfile)
except IOError:
pass
finally:
cfgfile.close
if __name__ == "__main__":
dbconf = checkconfig()
db = sqlite3.connect(dbconf)
main = MesCigares()
main.window.set_title(
"MesCigares - base : {0}".format(ntpath.basename(dbconf))
.replace('.db', ''))
gtk.main()