Primo commit
This commit is contained in:
parent
9667c241c1
commit
67de057e56
|
@ -0,0 +1,14 @@
|
||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# 2 space indentation
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
|
@ -0,0 +1,8 @@
|
||||||
|
[flake8]
|
||||||
|
# ignore =
|
||||||
|
# # indentation is not a multiple of four,
|
||||||
|
# E111,E114,
|
||||||
|
# # visually indented line with same indent as next logical line,
|
||||||
|
# E129
|
||||||
|
|
||||||
|
max-line-length=80
|
|
@ -0,0 +1,193 @@
|
||||||
|
# virtual env
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/emacs,python,visualstudiocode
|
||||||
|
# Edit at https://www.gitignore.io/?templates=emacs,python,visualstudiocode
|
||||||
|
|
||||||
|
### Emacs ###
|
||||||
|
# -*- mode: gitignore; -*-
|
||||||
|
*~
|
||||||
|
\#*\#
|
||||||
|
/.emacs.desktop
|
||||||
|
/.emacs.desktop.lock
|
||||||
|
*.elc
|
||||||
|
auto-save-list
|
||||||
|
tramp
|
||||||
|
.\#*
|
||||||
|
|
||||||
|
# Org-mode
|
||||||
|
.org-id-locations
|
||||||
|
*_archive
|
||||||
|
|
||||||
|
# flymake-mode
|
||||||
|
*_flymake.*
|
||||||
|
|
||||||
|
# eshell files
|
||||||
|
/eshell/history
|
||||||
|
/eshell/lastdir
|
||||||
|
|
||||||
|
# elpa packages
|
||||||
|
/elpa/
|
||||||
|
|
||||||
|
# reftex files
|
||||||
|
*.rel
|
||||||
|
|
||||||
|
# AUCTeX auto folder
|
||||||
|
/auto/
|
||||||
|
|
||||||
|
# cask packages
|
||||||
|
.cask/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Flycheck
|
||||||
|
flycheck_*.el
|
||||||
|
|
||||||
|
# server auth directory
|
||||||
|
/server/
|
||||||
|
|
||||||
|
# projectiles files
|
||||||
|
.projectile
|
||||||
|
|
||||||
|
# directory configuration
|
||||||
|
.dir-locals.el
|
||||||
|
|
||||||
|
# network security
|
||||||
|
/network-security.data
|
||||||
|
|
||||||
|
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don’t work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/emacs,python,visualstudiocode
|
|
@ -0,0 +1,6 @@
|
||||||
|
[style]
|
||||||
|
# YAPF uses the chromium style
|
||||||
|
# BASED_ON_STYLE = pep8
|
||||||
|
BASED_ON_STYLE = google
|
||||||
|
SPACES_BEFORE_COMMENT = 4
|
||||||
|
# SPLIT_BEFORE_LOGICAL_OPERATOR = true
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
MESSAGE_BOX_TITLE = "GOLEM-Iscritti"
|
|
@ -0,0 +1,30 @@
|
||||||
|
--
|
||||||
|
-- File generated with SQLiteStudio v3.2.1 on lun mag 6 20:53:06 2019
|
||||||
|
--
|
||||||
|
-- Text encoding used: UTF-8
|
||||||
|
--
|
||||||
|
PRAGMA foreign_keys = off;
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
-- Table: iscritti
|
||||||
|
CREATE TABLE iscritti (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
nome VARCHAR (50) NOT NULL,
|
||||||
|
cognome VARCHAR (50) NOT NULL,
|
||||||
|
data_nascita DATE NOT NULL,
|
||||||
|
comune_nascita VARCHAR (255) NOT NULL,
|
||||||
|
email VARCHAR (255) NOT NULL,
|
||||||
|
occupazione VARCHAR (50),
|
||||||
|
fonte VARCHAR (255),
|
||||||
|
data_firma DATE NOT NULL,
|
||||||
|
firma BLOB NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Table: sqlite_sequence
|
||||||
|
CREATE TABLE sqlite_sequence (
|
||||||
|
name,
|
||||||
|
seq
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT TRANSACTION;
|
||||||
|
PRAGMA foreign_keys = on;
|
|
@ -0,0 +1,622 @@
|
||||||
|
# Geraldo 20190506
|
||||||
|
# File originale nel repository di ActiveState su GitBub
|
||||||
|
# https://github.com/ActiveState/code/tree/master/recipes/Python/580725_Tkinter_Datepicker_like_jQuery_UI
|
||||||
|
|
||||||
|
# Log modifiche rispetto all'originale:
|
||||||
|
# -------------------------------------
|
||||||
|
# 20190506
|
||||||
|
# - Trim trailing space
|
||||||
|
# - Code style yapf->PEP8
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Author: Miguel Martinez Lopez
|
||||||
|
#
|
||||||
|
# Version: 1.0.7
|
||||||
|
#
|
||||||
|
# Uncomment the next line to see my email
|
||||||
|
# print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex")) # noqa
|
||||||
|
"""
|
||||||
|
These are the default bindings:
|
||||||
|
Click button 1 on entry: Show calendar
|
||||||
|
Click button 1 outsite calendar and entry: Hide calendar
|
||||||
|
Escape: Hide calendar
|
||||||
|
CTRL + PAGE UP: Move to the previous month.
|
||||||
|
CTRL + PAGE DOWN: Move to the next month.
|
||||||
|
CTRL + SHIFT + PAGE UP: Move to the previous year.
|
||||||
|
CTRL + SHIFT + PAGE DOWN: Move to the next year.
|
||||||
|
CTRL + LEFT: Move to the previous day.
|
||||||
|
CTRL + RIGHT: Move to the next day.
|
||||||
|
CTRL + UP: Move to the previous week.
|
||||||
|
CTRL + DOWN: Move to the next week.
|
||||||
|
CTRL + END: Close the datepicker and erase the date.
|
||||||
|
CTRL + HOME: Move to the current month.
|
||||||
|
CTRL + SPACE: Show date on calendar
|
||||||
|
CTRL + Return: Set current selection to entry
|
||||||
|
"""
|
||||||
|
|
||||||
|
import calendar
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
try:
|
||||||
|
import Tkinter
|
||||||
|
import tkFont
|
||||||
|
import ttk
|
||||||
|
|
||||||
|
from Tkconstants import CENTER, LEFT, N, E, W, S
|
||||||
|
from Tkinter import StringVar
|
||||||
|
except ImportError: # py3k
|
||||||
|
import tkinter as Tkinter
|
||||||
|
import tkinter.font as tkFont
|
||||||
|
import tkinter.ttk as ttk
|
||||||
|
|
||||||
|
from tkinter.constants import CENTER, LEFT, N, E, W, S
|
||||||
|
from tkinter import StringVar
|
||||||
|
|
||||||
|
|
||||||
|
def get_calendar(locale, fwday):
|
||||||
|
# instantiate proper calendar class
|
||||||
|
if locale is None:
|
||||||
|
return calendar.TextCalendar(fwday)
|
||||||
|
else:
|
||||||
|
return calendar.LocaleTextCalendar(fwday, locale)
|
||||||
|
|
||||||
|
|
||||||
|
class Calendar(ttk.Frame):
|
||||||
|
datetime = calendar.datetime.datetime
|
||||||
|
timedelta = calendar.datetime.timedelta
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
master=None,
|
||||||
|
year=None,
|
||||||
|
month=None,
|
||||||
|
firstweekday=calendar.MONDAY,
|
||||||
|
locale=None,
|
||||||
|
activebackground='#b1dcfb',
|
||||||
|
activeforeground='black',
|
||||||
|
selectbackground='#003eff',
|
||||||
|
selectforeground='white',
|
||||||
|
command=None,
|
||||||
|
borderwidth=1,
|
||||||
|
relief="solid",
|
||||||
|
on_click_month_button=None):
|
||||||
|
"""
|
||||||
|
WIDGET OPTIONS
|
||||||
|
|
||||||
|
locale, firstweekday, year, month, selectbackground,
|
||||||
|
selectforeground, activebackground, activeforeground,
|
||||||
|
command, borderwidth, relief, on_click_month_button
|
||||||
|
"""
|
||||||
|
|
||||||
|
if year is None:
|
||||||
|
year = self.datetime.now().year
|
||||||
|
|
||||||
|
if month is None:
|
||||||
|
month = self.datetime.now().month
|
||||||
|
|
||||||
|
self._selected_date = None
|
||||||
|
|
||||||
|
self._sel_bg = selectbackground
|
||||||
|
self._sel_fg = selectforeground
|
||||||
|
|
||||||
|
self._act_bg = activebackground
|
||||||
|
self._act_fg = activeforeground
|
||||||
|
|
||||||
|
self.on_click_month_button = on_click_month_button
|
||||||
|
|
||||||
|
self._selection_is_visible = False
|
||||||
|
self._command = command
|
||||||
|
|
||||||
|
ttk.Frame.__init__(
|
||||||
|
self, master, borderwidth=borderwidth, relief=relief)
|
||||||
|
|
||||||
|
self.bind("<FocusIn>",
|
||||||
|
lambda event: self.event_generate('<<DatePickerFocusIn>>'))
|
||||||
|
self.bind("<FocusOut>",
|
||||||
|
lambda event: self.event_generate('<<DatePickerFocusOut>>'))
|
||||||
|
|
||||||
|
self._cal = get_calendar(locale, firstweekday)
|
||||||
|
|
||||||
|
# custom ttk styles
|
||||||
|
style = ttk.Style()
|
||||||
|
style.layout('L.TButton', ([('Button.focus', {
|
||||||
|
'children': [('Button.leftarrow', None)]
|
||||||
|
})]))
|
||||||
|
style.layout('R.TButton', ([('Button.focus', {
|
||||||
|
'children': [('Button.rightarrow', None)]
|
||||||
|
})]))
|
||||||
|
|
||||||
|
self._font = tkFont.Font()
|
||||||
|
|
||||||
|
self._header_var = StringVar()
|
||||||
|
|
||||||
|
# header frame and its widgets
|
||||||
|
hframe = ttk.Frame(self)
|
||||||
|
lbtn = ttk.Button(
|
||||||
|
hframe, style='L.TButton', command=self._on_press_left_button)
|
||||||
|
lbtn.pack(side=LEFT)
|
||||||
|
|
||||||
|
self._header = ttk.Label(
|
||||||
|
hframe, width=15, anchor=CENTER, textvariable=self._header_var)
|
||||||
|
self._header.pack(side=LEFT, padx=12)
|
||||||
|
|
||||||
|
rbtn = ttk.Button(
|
||||||
|
hframe, style='R.TButton', command=self._on_press_right_button)
|
||||||
|
rbtn.pack(side=LEFT)
|
||||||
|
hframe.grid(columnspan=7, pady=4)
|
||||||
|
|
||||||
|
self._day_labels = {}
|
||||||
|
|
||||||
|
days_of_the_week = self._cal.formatweekheader(3).split()
|
||||||
|
|
||||||
|
for i, day_of_the_week in enumerate(days_of_the_week):
|
||||||
|
Tkinter.Label(
|
||||||
|
self, text=day_of_the_week, background='grey90').grid(
|
||||||
|
row=1, column=i, sticky=N + E + W + S)
|
||||||
|
|
||||||
|
for i in range(6):
|
||||||
|
for j in range(7):
|
||||||
|
self._day_labels[i, j] = label = Tkinter.Label(
|
||||||
|
self, background="white")
|
||||||
|
|
||||||
|
label.grid(row=i + 2, column=j, sticky=N + E + W + S)
|
||||||
|
label.bind("<Enter>",
|
||||||
|
lambda event: event.widget.configure(
|
||||||
|
background=self._act_bg,
|
||||||
|
foreground=self._act_fg))
|
||||||
|
label.bind(
|
||||||
|
"<Leave>",
|
||||||
|
lambda event: event.widget.configure(background="white"))
|
||||||
|
|
||||||
|
label.bind("<1>", self._pressed)
|
||||||
|
|
||||||
|
# adjust its columns width
|
||||||
|
font = tkFont.Font()
|
||||||
|
maxwidth = max(font.measure(text) for text in days_of_the_week)
|
||||||
|
for i in range(7):
|
||||||
|
self.grid_columnconfigure(i, minsize=maxwidth, weight=1)
|
||||||
|
|
||||||
|
self._year = None
|
||||||
|
self._month = None
|
||||||
|
|
||||||
|
# insert dates in the currently empty calendar
|
||||||
|
self._build_calendar(year, month)
|
||||||
|
|
||||||
|
def _build_calendar(self, year, month):
|
||||||
|
if not (self._year == year and self._month == month):
|
||||||
|
self._year = year
|
||||||
|
self._month = month
|
||||||
|
|
||||||
|
# update header text (Month, YEAR)
|
||||||
|
header = self._cal.formatmonthname(year, month, 0)
|
||||||
|
self._header_var.set(header.title())
|
||||||
|
|
||||||
|
# update calendar shown dates
|
||||||
|
cal = self._cal.monthdayscalendar(year, month)
|
||||||
|
|
||||||
|
for i in range(len(cal)):
|
||||||
|
|
||||||
|
week = cal[i]
|
||||||
|
fmt_week = [('%02d' % day) if day else '' for day in week]
|
||||||
|
|
||||||
|
for j, day_number in enumerate(fmt_week):
|
||||||
|
self._day_labels[i, j]["text"] = day_number
|
||||||
|
|
||||||
|
if len(cal) < 6:
|
||||||
|
for j in range(7):
|
||||||
|
self._day_labels[5, j]["text"] = ""
|
||||||
|
|
||||||
|
if (self._selected_date is not None
|
||||||
|
and self._selected_date.year == self._year
|
||||||
|
and self._selected_date.month == self._month):
|
||||||
|
self._show_selection()
|
||||||
|
|
||||||
|
def _find_label_coordinates(self, date):
|
||||||
|
first_weekday_of_the_month = (date.weekday() - date.day) % 7
|
||||||
|
|
||||||
|
return divmod(
|
||||||
|
(first_weekday_of_the_month - self._cal.firstweekday) % 7 +
|
||||||
|
date.day, 7)
|
||||||
|
|
||||||
|
def _show_selection(self):
|
||||||
|
"""Show a new selection."""
|
||||||
|
|
||||||
|
i, j = self._find_label_coordinates(self._selected_date)
|
||||||
|
|
||||||
|
label = self._day_labels[i, j]
|
||||||
|
|
||||||
|
label.configure(background=self._sel_bg, foreground=self._sel_fg)
|
||||||
|
|
||||||
|
label.unbind("<Enter>")
|
||||||
|
label.unbind("<Leave>")
|
||||||
|
|
||||||
|
self._selection_is_visible = True
|
||||||
|
|
||||||
|
def _clear_selection(self):
|
||||||
|
"""Show a new selection."""
|
||||||
|
i, j = self._find_label_coordinates(self._selected_date)
|
||||||
|
|
||||||
|
label = self._day_labels[i, j]
|
||||||
|
label.configure(background="white", foreground="black")
|
||||||
|
|
||||||
|
label.bind("<Enter>",
|
||||||
|
lambda event: event.widget.configure(
|
||||||
|
background=self._act_bg, foreground=self._act_fg))
|
||||||
|
label.bind(
|
||||||
|
"<Leave>",
|
||||||
|
lambda event: event.widget.configure(background="white"))
|
||||||
|
|
||||||
|
self._selection_is_visible = False
|
||||||
|
|
||||||
|
# Callback
|
||||||
|
|
||||||
|
def _pressed(self, evt):
|
||||||
|
"""Clicked somewhere in the calendar."""
|
||||||
|
|
||||||
|
text = evt.widget["text"]
|
||||||
|
|
||||||
|
if text == "":
|
||||||
|
return
|
||||||
|
|
||||||
|
day_number = int(text)
|
||||||
|
|
||||||
|
new_selected_date = datetime.datetime(self._year, self._month,
|
||||||
|
day_number)
|
||||||
|
if self._selected_date != new_selected_date:
|
||||||
|
if self._selected_date is not None:
|
||||||
|
self._clear_selection()
|
||||||
|
|
||||||
|
self._selected_date = new_selected_date
|
||||||
|
|
||||||
|
self._show_selection()
|
||||||
|
|
||||||
|
if self._command:
|
||||||
|
self._command(self._selected_date)
|
||||||
|
|
||||||
|
def _on_press_left_button(self):
|
||||||
|
self.prev_month()
|
||||||
|
|
||||||
|
if self.on_click_month_button is not None:
|
||||||
|
self.on_click_month_button()
|
||||||
|
|
||||||
|
def _on_press_right_button(self):
|
||||||
|
self.next_month()
|
||||||
|
|
||||||
|
if self.on_click_month_button is not None:
|
||||||
|
self.on_click_month_button()
|
||||||
|
|
||||||
|
def select_prev_day(self):
|
||||||
|
"""Updated calendar to show the previous day."""
|
||||||
|
if self._selected_date is None:
|
||||||
|
self._selected_date = datetime.datetime(self._year, self._month, 1)
|
||||||
|
else:
|
||||||
|
self._clear_selection()
|
||||||
|
self._selected_date = self._selected_date - self.timedelta(days=1)
|
||||||
|
|
||||||
|
self._build_calendar(
|
||||||
|
self._selected_date.year,
|
||||||
|
self._selected_date.month) # reconstruct calendar
|
||||||
|
|
||||||
|
def select_next_day(self):
|
||||||
|
"""Update calendar to show the next day."""
|
||||||
|
|
||||||
|
if self._selected_date is None:
|
||||||
|
self._selected_date = datetime.datetime(self._year, self._month, 1)
|
||||||
|
else:
|
||||||
|
self._clear_selection()
|
||||||
|
self._selected_date = self._selected_date + self.timedelta(days=1)
|
||||||
|
|
||||||
|
self._build_calendar(
|
||||||
|
self._selected_date.year,
|
||||||
|
self._selected_date.month) # reconstruct calendar
|
||||||
|
|
||||||
|
def select_prev_week_day(self):
|
||||||
|
"""Updated calendar to show the previous week."""
|
||||||
|
if self._selected_date is None:
|
||||||
|
self._selected_date = datetime.datetime(self._year, self._month, 1)
|
||||||
|
else:
|
||||||
|
self._clear_selection()
|
||||||
|
self._selected_date = self._selected_date - self.timedelta(days=7)
|
||||||
|
|
||||||
|
self._build_calendar(
|
||||||
|
self._selected_date.year,
|
||||||
|
self._selected_date.month) # reconstruct calendar
|
||||||
|
|
||||||
|
def select_next_week_day(self):
|
||||||
|
"""Update calendar to show the next week."""
|
||||||
|
if self._selected_date is None:
|
||||||
|
self._selected_date = datetime.datetime(self._year, self._month, 1)
|
||||||
|
else:
|
||||||
|
self._clear_selection()
|
||||||
|
self._selected_date = self._selected_date + self.timedelta(days=7)
|
||||||
|
|
||||||
|
self._build_calendar(
|
||||||
|
self._selected_date.year,
|
||||||
|
self._selected_date.month) # reconstruct calendar
|
||||||
|
|
||||||
|
def select_current_date(self):
|
||||||
|
"""Update calendar to current date."""
|
||||||
|
if self._selection_is_visible:
|
||||||
|
self._clear_selection()
|
||||||
|
|
||||||
|
self._selected_date = datetime.datetime.now()
|
||||||
|
self._build_calendar(self._selected_date.year,
|
||||||
|
self._selected_date.month)
|
||||||
|
|
||||||
|
def prev_month(self):
|
||||||
|
"""Updated calendar to show the previous week."""
|
||||||
|
if self._selection_is_visible:
|
||||||
|
self._clear_selection()
|
||||||
|
|
||||||
|
date = self.datetime(self._year, self._month,
|
||||||
|
1) - self.timedelta(days=1)
|
||||||
|
self._build_calendar(date.year, date.month) # reconstuct calendar
|
||||||
|
|
||||||
|
def next_month(self):
|
||||||
|
"""Update calendar to show the next month."""
|
||||||
|
if self._selection_is_visible:
|
||||||
|
self._clear_selection()
|
||||||
|
|
||||||
|
date = self.datetime(self._year, self._month, 1) + self.timedelta(
|
||||||
|
days=calendar.monthrange(self._year, self._month)[1] + 1)
|
||||||
|
|
||||||
|
self._build_calendar(date.year, date.month) # reconstuct calendar
|
||||||
|
|
||||||
|
def prev_year(self):
|
||||||
|
"""Updated calendar to show the previous year."""
|
||||||
|
|
||||||
|
if self._selection_is_visible:
|
||||||
|
self._clear_selection()
|
||||||
|
|
||||||
|
self._build_calendar(self._year - 1,
|
||||||
|
self._month) # reconstruct calendar
|
||||||
|
|
||||||
|
def next_year(self):
|
||||||
|
"""Update calendar to show the next year."""
|
||||||
|
|
||||||
|
if self._selection_is_visible:
|
||||||
|
self._clear_selection()
|
||||||
|
|
||||||
|
self._build_calendar(self._year + 1,
|
||||||
|
self._month) # reconstruct calendar
|
||||||
|
|
||||||
|
def get_selection(self):
|
||||||
|
"""Return a datetime representing the current selected date."""
|
||||||
|
return self._selected_date
|
||||||
|
|
||||||
|
selection = get_selection
|
||||||
|
|
||||||
|
def set_selection(self, date):
|
||||||
|
"""Set the selected date."""
|
||||||
|
if self._selected_date is not None and self._selected_date != date:
|
||||||
|
self._clear_selection()
|
||||||
|
|
||||||
|
self._selected_date = date
|
||||||
|
|
||||||
|
self._build_calendar(date.year, date.month) # reconstruct calendar
|
||||||
|
|
||||||
|
|
||||||
|
# see this URL for date format information:
|
||||||
|
# https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
|
||||||
|
|
||||||
|
|
||||||
|
class Datepicker(ttk.Entry):
|
||||||
|
def __init__(self,
|
||||||
|
master,
|
||||||
|
entrywidth=None,
|
||||||
|
entrystyle=None,
|
||||||
|
datevar=None,
|
||||||
|
dateformat="%Y-%m-%d",
|
||||||
|
onselect=None,
|
||||||
|
firstweekday=calendar.MONDAY,
|
||||||
|
locale=None,
|
||||||
|
activebackground='#b1dcfb',
|
||||||
|
activeforeground='black',
|
||||||
|
selectbackground='#003eff',
|
||||||
|
selectforeground='white',
|
||||||
|
borderwidth=1,
|
||||||
|
relief="solid"):
|
||||||
|
|
||||||
|
if datevar is not None:
|
||||||
|
self.date_var = datevar
|
||||||
|
else:
|
||||||
|
self.date_var = Tkinter.StringVar()
|
||||||
|
|
||||||
|
entry_config = {}
|
||||||
|
if entrywidth is not None:
|
||||||
|
entry_config["width"] = entrywidth
|
||||||
|
|
||||||
|
if entrystyle is not None:
|
||||||
|
entry_config["style"] = entrystyle
|
||||||
|
|
||||||
|
ttk.Entry.__init__(
|
||||||
|
self, master, textvariable=self.date_var, **entry_config)
|
||||||
|
|
||||||
|
self.date_format = dateformat
|
||||||
|
|
||||||
|
self._is_calendar_visible = False
|
||||||
|
self._on_select_date_command = onselect
|
||||||
|
|
||||||
|
self.calendar_frame = Calendar(
|
||||||
|
self.winfo_toplevel(),
|
||||||
|
firstweekday=firstweekday,
|
||||||
|
locale=locale,
|
||||||
|
activebackground=activebackground,
|
||||||
|
activeforeground=activeforeground,
|
||||||
|
selectbackground=selectbackground,
|
||||||
|
selectforeground=selectforeground,
|
||||||
|
command=self._on_selected_date,
|
||||||
|
on_click_month_button=lambda: self.focus())
|
||||||
|
|
||||||
|
self.bind_all("<1>", self._on_click, "+")
|
||||||
|
|
||||||
|
self.bind("<FocusOut>", lambda event: self._on_entry_focus_out())
|
||||||
|
self.bind("<Escape>", lambda event: self.hide_calendar())
|
||||||
|
self.calendar_frame.bind("<<DatePickerFocusOut>>",
|
||||||
|
lambda event: self._on_calendar_focus_out())
|
||||||
|
|
||||||
|
# CTRL + PAGE UP: Move to the previous month.
|
||||||
|
self.bind("<Control-Prior>",
|
||||||
|
lambda event: self.calendar_frame.prev_month())
|
||||||
|
|
||||||
|
# CTRL + PAGE DOWN: Move to the next month.
|
||||||
|
self.bind("<Control-Next>",
|
||||||
|
lambda event: self.calendar_frame.next_month())
|
||||||
|
|
||||||
|
# CTRL + SHIFT + PAGE UP: Move to the previous year.
|
||||||
|
self.bind("<Control-Shift-Prior>",
|
||||||
|
lambda event: self.calendar_frame.prev_year())
|
||||||
|
|
||||||
|
# CTRL + SHIFT + PAGE DOWN: Move to the next year.
|
||||||
|
self.bind("<Control-Shift-Next>",
|
||||||
|
lambda event: self.calendar_frame.next_year())
|
||||||
|
|
||||||
|
# CTRL + LEFT: Move to the previous day.
|
||||||
|
self.bind("<Control-Left>",
|
||||||
|
lambda event: self.calendar_frame.select_prev_day())
|
||||||
|
|
||||||
|
# CTRL + RIGHT: Move to the next day.
|
||||||
|
self.bind("<Control-Right>",
|
||||||
|
lambda event: self.calendar_frame.select_next_day())
|
||||||
|
|
||||||
|
# CTRL + UP: Move to the previous week.
|
||||||
|
self.bind("<Control-Up>",
|
||||||
|
lambda event: self.calendar_frame.select_prev_week_day())
|
||||||
|
|
||||||
|
# CTRL + DOWN: Move to the next week.
|
||||||
|
self.bind("<Control-Down>",
|
||||||
|
lambda event: self.calendar_frame.select_next_week_day())
|
||||||
|
|
||||||
|
# CTRL + END: Close the datepicker and erase the date.
|
||||||
|
self.bind("<Control-End>", lambda event: self.erase())
|
||||||
|
|
||||||
|
# CTRL + HOME: Move to the current month.
|
||||||
|
self.bind("<Control-Home>",
|
||||||
|
lambda event: self.calendar_frame.select_current_date())
|
||||||
|
|
||||||
|
# CTRL + SPACE: Show date on calendar
|
||||||
|
self.bind("<Control-space>",
|
||||||
|
lambda event: self.show_date_on_calendar())
|
||||||
|
|
||||||
|
# CTRL + Return: Set to entry current selection
|
||||||
|
self.bind("<Control-Return>",
|
||||||
|
lambda event: self.set_date_from_calendar())
|
||||||
|
|
||||||
|
def set_date_from_calendar(self):
|
||||||
|
if self.is_calendar_visible:
|
||||||
|
selected_date = self.calendar_frame.selection()
|
||||||
|
|
||||||
|
if selected_date is not None:
|
||||||
|
self.date_var.set(selected_date.strftime(self.date_format))
|
||||||
|
|
||||||
|
if self._on_select_date_command is not None:
|
||||||
|
self._on_select_date_command(selected_date)
|
||||||
|
|
||||||
|
self.hide_calendar()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_text(self):
|
||||||
|
return self.date_var.get()
|
||||||
|
|
||||||
|
@current_text.setter
|
||||||
|
def current_text(self, text):
|
||||||
|
return self.date_var.set(text)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_date(self):
|
||||||
|
try:
|
||||||
|
date = datetime.datetime.strptime(self.date_var.get(),
|
||||||
|
self.date_format)
|
||||||
|
return date
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@current_date.setter
|
||||||
|
def current_date(self, date):
|
||||||
|
self.date_var.set(date.strftime(self.date_format))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_valid_date(self):
|
||||||
|
if self.current_date is None:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def show_date_on_calendar(self):
|
||||||
|
date = self.current_date
|
||||||
|
if date is not None:
|
||||||
|
self.calendar_frame.set_selection(date)
|
||||||
|
|
||||||
|
self.show_calendar()
|
||||||
|
|
||||||
|
def show_calendar(self):
|
||||||
|
if not self._is_calendar_visible:
|
||||||
|
self.calendar_frame.place(in_=self, relx=0, rely=1)
|
||||||
|
self.calendar_frame.lift()
|
||||||
|
|
||||||
|
self._is_calendar_visible = True
|
||||||
|
|
||||||
|
def hide_calendar(self):
|
||||||
|
if self._is_calendar_visible:
|
||||||
|
self.calendar_frame.place_forget()
|
||||||
|
|
||||||
|
self._is_calendar_visible = False
|
||||||
|
|
||||||
|
def erase(self):
|
||||||
|
self.hide_calendar()
|
||||||
|
self.date_var.set("")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_calendar_visible(self):
|
||||||
|
return self._is_calendar_visible
|
||||||
|
|
||||||
|
def _on_entry_focus_out(self):
|
||||||
|
if not str(self.focus_get()).startswith(str(self.calendar_frame)):
|
||||||
|
self.hide_calendar()
|
||||||
|
|
||||||
|
def _on_calendar_focus_out(self):
|
||||||
|
if self.focus_get() != self:
|
||||||
|
self.hide_calendar()
|
||||||
|
|
||||||
|
def _on_selected_date(self, date):
|
||||||
|
self.date_var.set(date.strftime(self.date_format))
|
||||||
|
self.hide_calendar()
|
||||||
|
|
||||||
|
if self._on_select_date_command is not None:
|
||||||
|
self._on_select_date_command(date)
|
||||||
|
|
||||||
|
def _on_click(self, event):
|
||||||
|
str_widget = str(event.widget)
|
||||||
|
|
||||||
|
if str_widget == str(self):
|
||||||
|
if not self._is_calendar_visible:
|
||||||
|
self.show_date_on_calendar()
|
||||||
|
else:
|
||||||
|
if not str_widget.startswith(str(
|
||||||
|
self.calendar_frame)) and self._is_calendar_visible:
|
||||||
|
self.hide_calendar()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
from Tkinter import Tk, Frame, Label
|
||||||
|
except ImportError:
|
||||||
|
from tkinter import Tk, Frame, Label
|
||||||
|
|
||||||
|
root = Tk()
|
||||||
|
root.geometry("500x600")
|
||||||
|
|
||||||
|
main = Frame(root, pady=15, padx=15)
|
||||||
|
main.pack(expand=True, fill="both")
|
||||||
|
|
||||||
|
Label(main, justify="left", text=__doc__).pack(anchor="w", pady=(0, 15))
|
||||||
|
|
||||||
|
Datepicker(main).pack(anchor="w")
|
||||||
|
|
||||||
|
if 'win' not in sys.platform:
|
||||||
|
style = ttk.Style()
|
||||||
|
style.theme_use('clam')
|
||||||
|
|
||||||
|
root.mainloop()
|
|
@ -0,0 +1,53 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import io
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
class Db():
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.dbfile = "iscritti.sqlite3"
|
||||||
|
|
||||||
|
def insert_new(self, iscritto):
|
||||||
|
firma_stream = io.BytesIO()
|
||||||
|
iscritto.img_firma.save(firma_stream, format="PNG")
|
||||||
|
firma_bytes = firma_stream.getvalue()
|
||||||
|
conn = sqlite3.connect(self.dbfile)
|
||||||
|
cur = conn.cursor()
|
||||||
|
# Come salvo il contenuto della Canvas
|
||||||
|
# nel db che non ce' una "textvar" associata?
|
||||||
|
par = (
|
||||||
|
iscritto.nome.get(),
|
||||||
|
iscritto.cognome.get(),
|
||||||
|
iscritto.data_nascita.get(),
|
||||||
|
iscritto.comune_nascita.get(),
|
||||||
|
iscritto.email.get(),
|
||||||
|
iscritto.occupazione.get(),
|
||||||
|
iscritto.fonte.get(),
|
||||||
|
iscritto.data_firma.get(),
|
||||||
|
firma_bytes)
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO iscritti ("
|
||||||
|
"nome"
|
||||||
|
", cognome"
|
||||||
|
", data_nascita"
|
||||||
|
", comune_nascita"
|
||||||
|
", email"
|
||||||
|
", occupazione"
|
||||||
|
", fonte"
|
||||||
|
", data_firma"
|
||||||
|
", firma"
|
||||||
|
") values ("
|
||||||
|
"?" # nome
|
||||||
|
", ?" # cognome
|
||||||
|
", ?" # data_nascita
|
||||||
|
", ?" # comune_nascita
|
||||||
|
", ?" # email
|
||||||
|
", ?" # occupazione
|
||||||
|
", ?" # fonte
|
||||||
|
", ?" # data_firma
|
||||||
|
", ?" # firma
|
||||||
|
");")
|
||||||
|
cur.execute(sql, par)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
|
@ -0,0 +1,15 @@
|
||||||
|
appdirs==1.4.3
|
||||||
|
attrs==19.1.0
|
||||||
|
autopep8==1.4.4
|
||||||
|
black==19.3b0
|
||||||
|
Click==7.0
|
||||||
|
entrypoints==0.3
|
||||||
|
flake8==3.7.7
|
||||||
|
jedi==0.13.3
|
||||||
|
mccabe==0.6.1
|
||||||
|
parso==0.4.0
|
||||||
|
pycodestyle==2.5.0
|
||||||
|
pyflakes==2.1.1
|
||||||
|
rope==0.14.0
|
||||||
|
toml==0.10.0
|
||||||
|
yapf==0.27.0
|
|
@ -0,0 +1,502 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Per la generazione dei reports:
|
||||||
|
# - https://www.reportlab.com/
|
||||||
|
# - https://pythonhosted.org/PollyReports/tutorial.html
|
||||||
|
# - https://pbpython.com/pdf-reports.html
|
||||||
|
|
||||||
|
# Smart Card
|
||||||
|
# - https://pyscard.sourceforge.io/
|
||||||
|
# - https://www.mmxforge.net/index.php/sviluppo/python/item/9-lettura-dei-dati-della-tessera-sanitaria-con-python
|
||||||
|
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
import tkinter.ttk as ttk
|
||||||
|
import tkinter.messagebox as tkMessagebox
|
||||||
|
|
||||||
|
from locale import (setlocale, LC_ALL)
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageDraw
|
||||||
|
from PIL import ImageTk
|
||||||
|
|
||||||
|
from db import Db
|
||||||
|
from datepicker import Datepicker
|
||||||
|
from const import MESSAGE_BOX_TITLE
|
||||||
|
from iscritto import Iscritto
|
||||||
|
|
||||||
|
FRM_PADX = 5
|
||||||
|
FRM_PADY = 3
|
||||||
|
|
||||||
|
FRA_PADX = 5
|
||||||
|
FRA_PADY = 3
|
||||||
|
|
||||||
|
LBL_PADX = 3
|
||||||
|
LBL_PADY = 3
|
||||||
|
|
||||||
|
TXT_PADX = 5
|
||||||
|
TXT_PADY = 3
|
||||||
|
|
||||||
|
IMG_PADX = 5
|
||||||
|
IMG_PADY = 3
|
||||||
|
|
||||||
|
BTN_PADX = 5
|
||||||
|
BTN_PADY = 3
|
||||||
|
|
||||||
|
setlocale(LC_ALL, '')
|
||||||
|
|
||||||
|
class Iscritti():
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
self.parent = parent
|
||||||
|
self.larghezza_firma = 800
|
||||||
|
self.altezza_firma = 300
|
||||||
|
self.img_firma_color_fg = "black"
|
||||||
|
self.img_firma_color_bg = "white"
|
||||||
|
self.img_firma_penwidth = 5
|
||||||
|
self.iscritto = Iscritto(self.larghezza_firma,
|
||||||
|
self.altezza_firma,
|
||||||
|
self.img_firma_color_fg,
|
||||||
|
self.img_firma_color_bg)
|
||||||
|
self.create_widgets()
|
||||||
|
parent.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
def create_widgets(self):
|
||||||
|
self.configure_master()
|
||||||
|
|
||||||
|
self.crea_master_widgets()
|
||||||
|
return
|
||||||
|
|
||||||
|
def configure_master(self):
|
||||||
|
# Crea la finestra a tutto schermo
|
||||||
|
# (non funziona su Mint 19.1)
|
||||||
|
# self.parent.wm_state('zoomed')
|
||||||
|
self.parent.focus_set()
|
||||||
|
self.parent.title("GOLEM - Iscritti")
|
||||||
|
self.parent.rowconfigure(0, weight=1)
|
||||||
|
self.parent.columnconfigure(0, weight=1)
|
||||||
|
self.parent.protocol("WM_DELETE_WINDOW", self.esci)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_master_widgets(self):
|
||||||
|
self.fra_master = ttk.Frame(self.parent)
|
||||||
|
|
||||||
|
row_number = 0
|
||||||
|
|
||||||
|
s1 = ttk.Separator(self.fra_master, orient=tk.HORIZONTAL)
|
||||||
|
s1.grid(row=row_number, column=0, sticky=tk.W + tk.E)
|
||||||
|
row_number += 1
|
||||||
|
|
||||||
|
self.crea_frame_pulsanti(self.fra_master, row_number)
|
||||||
|
self.fra_master.rowconfigure(row_number, weight=0)
|
||||||
|
row_number += 1
|
||||||
|
|
||||||
|
s2 = ttk.Separator(self.fra_master, orient=tk.HORIZONTAL)
|
||||||
|
s2.grid(row=row_number, column=0, sticky=tk.W + tk.E)
|
||||||
|
row_number += 1
|
||||||
|
|
||||||
|
self.crea_frame_dati(self.fra_master, row_number)
|
||||||
|
self.fra_master.rowconfigure(row_number, weight=1)
|
||||||
|
row_number += 1
|
||||||
|
|
||||||
|
self.fra_master.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
self.fra_master.grid(row=0,
|
||||||
|
column=0,
|
||||||
|
padx=FRA_PADX,
|
||||||
|
pady=FRA_PADX,
|
||||||
|
sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_frame_pulsanti(self, parent, row_number):
|
||||||
|
self.fra_pulsanti = ttk.Frame(parent)
|
||||||
|
self.fra_pulsanti.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=FRA_PADX,
|
||||||
|
pady=FRA_PADX,
|
||||||
|
sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
|
||||||
|
child_row = 0
|
||||||
|
|
||||||
|
self.crea_widgets_pulsanti(self.fra_pulsanti, child_row)
|
||||||
|
self.fra_pulsanti.rowconfigure(0, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_pulsanti(self, parent, row_number):
|
||||||
|
child_column = 0
|
||||||
|
self.btn_nuovo = ttk.Button(parent, text="Nuovo", command=self.nuovo)
|
||||||
|
self.btn_nuovo.grid(row=row_number,
|
||||||
|
column=child_column,
|
||||||
|
padx=BTN_PADX,
|
||||||
|
pady=BTN_PADX,
|
||||||
|
sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
parent.columnconfigure(child_column, weight=1)
|
||||||
|
|
||||||
|
# Funzionalita' ricerca da implementare (datagrid in tkinter???)
|
||||||
|
# child_column += 1
|
||||||
|
# self.btn_cerca = ttk.Button(parent, text="Cerca", command=self.cerca)
|
||||||
|
# self.btn_cerca.grid(row=row_number,
|
||||||
|
# column=child_column,
|
||||||
|
# padx=BTN_PADX,
|
||||||
|
# pady=BTN_PADX,
|
||||||
|
# sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
# parent.columnconfigure(child_column, weight=1)
|
||||||
|
|
||||||
|
child_column += 1
|
||||||
|
self.btn_salva = ttk.Button(parent, text="Salva", command=self.salva)
|
||||||
|
self.btn_salva.grid(row=row_number,
|
||||||
|
column=child_column,
|
||||||
|
padx=BTN_PADX,
|
||||||
|
pady=BTN_PADX,
|
||||||
|
sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
parent.columnconfigure(child_column, weight=1)
|
||||||
|
|
||||||
|
child_column += 1
|
||||||
|
self.btn_esci = ttk.Button(parent, text="Esci", command=self.esci)
|
||||||
|
self.btn_esci.grid(row=row_number,
|
||||||
|
column=child_column,
|
||||||
|
padx=BTN_PADX,
|
||||||
|
pady=BTN_PADX,
|
||||||
|
sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
parent.columnconfigure(child_column, weight=1)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_frame_dati(self, parent, row_number):
|
||||||
|
self.fra_dati = ttk.Frame(parent)
|
||||||
|
self.fra_dati.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=FRA_PADX,
|
||||||
|
pady=FRA_PADX,
|
||||||
|
sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
|
||||||
|
child_row = 0
|
||||||
|
|
||||||
|
self.crea_widgets_id(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_cognome(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_nome(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_data_nascita(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_comune_nascita(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_email(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_occupazione(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_fonte(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_data_firma(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.crea_widgets_firma(self.fra_dati, child_row)
|
||||||
|
self.fra_dati.rowconfigure(child_row, weight=1)
|
||||||
|
child_row += 1
|
||||||
|
|
||||||
|
self.fra_dati.columnconfigure(0, weight=0)
|
||||||
|
self.fra_dati.columnconfigure(1, weight=1)
|
||||||
|
self.fra_dati.columnconfigure(2, weight=1)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_id(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_id = ttk.Label(parent, text="Id", anchor=tk.W)
|
||||||
|
self.lbl_id.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_id = ttk.Entry(parent,
|
||||||
|
textvariable=self.iscritto.id,
|
||||||
|
state="readonly")
|
||||||
|
self.txt_id.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_cognome(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_cognome = ttk.Label(parent, text="Cognome", anchor=tk.W)
|
||||||
|
self.lbl_cognome.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_cognome = ttk.Entry(parent, textvariable=self.iscritto.cognome)
|
||||||
|
self.txt_cognome.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_nome(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_nome = ttk.Label(parent, text="Nome", anchor=tk.W)
|
||||||
|
self.lbl_nome.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_nome = ttk.Entry(parent, textvariable=self.iscritto.nome)
|
||||||
|
self.txt_nome.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_data_nascita(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_data_nascita = ttk.Label(parent,
|
||||||
|
text="Data di nascita",
|
||||||
|
anchor=tk.W)
|
||||||
|
self.lbl_data_nascita.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_data_nascita = Datepicker(parent,
|
||||||
|
dateformat="%x",
|
||||||
|
datevar=self.iscritto.data_nascita)
|
||||||
|
self.txt_data_nascita.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_comune_nascita(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_comune_nascita = ttk.Label(parent,
|
||||||
|
text="Comune di nascita",
|
||||||
|
anchor=tk.W)
|
||||||
|
self.lbl_comune_nascita.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_comune_nascita = ttk.Entry(
|
||||||
|
parent, textvariable=self.iscritto.comune_nascita)
|
||||||
|
self.txt_comune_nascita.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_email(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_email = ttk.Label(parent, text="E-mail", anchor=tk.W)
|
||||||
|
self.lbl_email.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_email = ttk.Entry(parent, textvariable=self.iscritto.email)
|
||||||
|
self.txt_email.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_occupazione(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_occupazione = ttk.Label(parent,
|
||||||
|
text="Occupazione",
|
||||||
|
anchor=tk.W)
|
||||||
|
self.lbl_occupazione.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_occupazione = ttk.Entry(parent,
|
||||||
|
textvariable=self.iscritto.occupazione)
|
||||||
|
self.txt_occupazione.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_fonte(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_fonte = ttk.Label(parent, text="Fonte", anchor=tk.W)
|
||||||
|
self.lbl_fonte.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_fonte = ttk.Entry(parent, textvariable=self.iscritto.fonte)
|
||||||
|
self.txt_fonte.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_data_firma(self, parent, row_number):
|
||||||
|
|
||||||
|
self.lbl_data_firma = ttk.Label(parent, text="Data firma", anchor=tk.W)
|
||||||
|
self.lbl_data_firma.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
|
||||||
|
self.txt_data_firma = Datepicker(parent,
|
||||||
|
dateformat="%x",
|
||||||
|
datevar=self.iscritto.data_firma)
|
||||||
|
self.txt_data_firma.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
columnspan=2,
|
||||||
|
padx=TXT_PADX,
|
||||||
|
pady=TXT_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
return
|
||||||
|
|
||||||
|
def crea_widgets_firma(self, parent, row_number):
|
||||||
|
# https://www.youtube.com/watch?v=rhvJ_M218EQ
|
||||||
|
self.img_firma_old_x = None
|
||||||
|
self.img_firma_old_y = None
|
||||||
|
|
||||||
|
self.lbl_firma = ttk.Label(parent, text="Firma", anchor=tk.N + tk.W)
|
||||||
|
self.lbl_firma.grid(row=row_number,
|
||||||
|
column=0,
|
||||||
|
padx=LBL_PADX,
|
||||||
|
pady=LBL_PADY,
|
||||||
|
sticky=tk.N + tk.S + tk.E + tk.W)
|
||||||
|
larghezza = self.larghezza_firma
|
||||||
|
altezza = self.altezza_firma
|
||||||
|
self.cv_firma = tk.Canvas(
|
||||||
|
parent,
|
||||||
|
width=larghezza,
|
||||||
|
height=altezza,
|
||||||
|
border=1,
|
||||||
|
background=self.img_firma_color_bg,
|
||||||
|
relief=tk.SUNKEN)
|
||||||
|
self.cv_firma.bind("<B1-Motion>", self.img_firma_paint)
|
||||||
|
self.cv_firma.bind("<ButtonRelease-1>", self.img_firma_reset)
|
||||||
|
self.cv_firma.grid(row=row_number,
|
||||||
|
column=1,
|
||||||
|
padx=IMG_PADX,
|
||||||
|
pady=IMG_PADY,
|
||||||
|
sticky=tk.N + tk.W)
|
||||||
|
self.draw_frirma = ImageDraw.Draw(self.iscritto.img_firma)
|
||||||
|
|
||||||
|
self.fra_pulsanti_firma = ttk.Frame(parent)
|
||||||
|
self.fra_pulsanti_firma.columnconfigure(0, weight=1)
|
||||||
|
self.fra_pulsanti_firma.grid(row=row_number,
|
||||||
|
column=2,
|
||||||
|
padx=FRA_PADX,
|
||||||
|
pady=FRA_PADX,
|
||||||
|
sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
|
||||||
|
self.btn_firma_clear = ttk.Button(self.fra_pulsanti_firma,
|
||||||
|
text="Pulisci")
|
||||||
|
self.btn_firma_clear.bind("<Button-1>", self.img_firma_clear)
|
||||||
|
self.btn_firma_clear.grid(row=0,
|
||||||
|
column=0,
|
||||||
|
padx=BTN_PADX,
|
||||||
|
pady=BTN_PADX,
|
||||||
|
sticky=tk.N + tk.W + tk.S + tk.E)
|
||||||
|
return
|
||||||
|
|
||||||
|
def img_firma_paint(self, e):
|
||||||
|
if self.img_firma_old_x and self.img_firma_old_y:
|
||||||
|
self.cv_firma.create_line(self.img_firma_old_x,
|
||||||
|
self.img_firma_old_y,
|
||||||
|
e.x,
|
||||||
|
e.y,
|
||||||
|
width=self.img_firma_penwidth,
|
||||||
|
fill=self.img_firma_color_fg,
|
||||||
|
capstyle=tk.ROUND,
|
||||||
|
smooth=True)
|
||||||
|
xy = [self.img_firma_old_x, self.img_firma_old_y, e.x, e.y]
|
||||||
|
self.draw_frirma.line(xy,
|
||||||
|
width=self.img_firma_penwidth,
|
||||||
|
fill=self.img_firma_color_fg)
|
||||||
|
self.img_firma_old_x = e.x
|
||||||
|
self.img_firma_old_y = e.y
|
||||||
|
return
|
||||||
|
|
||||||
|
def img_firma_reset(self, e):
|
||||||
|
self.img_firma_old_x = None
|
||||||
|
self.img_firma_old_y = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def img_firma_clear(self, e):
|
||||||
|
self.cv_firma.delete("all")
|
||||||
|
return
|
||||||
|
|
||||||
|
def nuovo(self):
|
||||||
|
self.iscritto.new()
|
||||||
|
# # Alla canvas non e' associata nessuna tkinger.Variable()
|
||||||
|
# # quindi lavoro direttamente sul widget
|
||||||
|
self.cv_firma.delete("all")
|
||||||
|
self.draw_frirma = ImageDraw.Draw(self.iscritto.img_firma)
|
||||||
|
return
|
||||||
|
|
||||||
|
def cerca(self):
|
||||||
|
tkMessagebox.showinfo(MESSAGE_BOX_TITLE, "valido, salvo!")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def salva(self):
|
||||||
|
if self.iscritto.is_valid():
|
||||||
|
salva = tkMessagebox.askyesno(MESSAGE_BOX_TITLE,
|
||||||
|
"Convermi salvataggio?")
|
||||||
|
if salva:
|
||||||
|
db = Db()
|
||||||
|
db.insert_new(self.iscritto)
|
||||||
|
self.nuovo()
|
||||||
|
else:
|
||||||
|
tkMessagebox.showinfo(MESSAGE_BOX_TITLE,
|
||||||
|
"non valido e quindi non posso salvare!")
|
||||||
|
self.parent.update()
|
||||||
|
|
||||||
|
def esci(self):
|
||||||
|
if tkMessagebox.askyesno(MESSAGE_BOX_TITLE, "Esco?"):
|
||||||
|
self.parent.update()
|
||||||
|
self.parent.destroy()
|
||||||
|
return
|
Binary file not shown.
|
@ -0,0 +1,146 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
import tkinter.messagebox as tkMessagebox
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from const import MESSAGE_BOX_TITLE
|
||||||
|
|
||||||
|
class Iscritto():
|
||||||
|
|
||||||
|
def __init__(self, larghezza_firma, altezza_firma, img_firma_color_fg, img_firma_color_bg):
|
||||||
|
self.larghezza_firma = larghezza_firma
|
||||||
|
self.altezza_firma = altezza_firma
|
||||||
|
self.img_firma_color_fg = img_firma_color_fg
|
||||||
|
self.img_firma_color_bg = img_firma_color_bg
|
||||||
|
self.id = tk.StringVar()
|
||||||
|
self.cognome = tk.StringVar()
|
||||||
|
self.nome = tk.StringVar()
|
||||||
|
self.data_nascita = tk.StringVar()
|
||||||
|
self.comune_nascita = tk.StringVar()
|
||||||
|
self.email = tk.StringVar()
|
||||||
|
self.occupazione = tk.StringVar()
|
||||||
|
self.fonte = tk.StringVar()
|
||||||
|
self.data_firma = tk.StringVar()
|
||||||
|
# In Image.new(), se non indicato il "color" e' nero
|
||||||
|
# "L" = 8 bit - B/N
|
||||||
|
self.img_firma = Image.new("L", (self.larghezza_firma, self.altezza_firma), self.img_firma_color_bg)
|
||||||
|
return
|
||||||
|
|
||||||
|
def new(self):
|
||||||
|
self.id.set("")
|
||||||
|
self.cognome.set("")
|
||||||
|
self.nome.set("")
|
||||||
|
self.data_nascita.set("")
|
||||||
|
self.comune_nascita.set("")
|
||||||
|
self.email.set("")
|
||||||
|
self.occupazione.set("")
|
||||||
|
self.fonte.set("")
|
||||||
|
self.data_firma.set("")
|
||||||
|
# In Image.new(), se non indicato il "color" e' nero
|
||||||
|
# "L" = 8 bit - B/N
|
||||||
|
self.img_firma = Image.new("L", (self.larghezza_firma, self.altezza_firma), self.img_firma_color_bg)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
is_valid = self.cognome_is_valid()
|
||||||
|
if is_valid:
|
||||||
|
is_valid = self.nome_is_valid()
|
||||||
|
if is_valid:
|
||||||
|
is_valid = self.data_nascita_is_valid()
|
||||||
|
if is_valid:
|
||||||
|
is_valid = self.comune_nascita_is_valid()
|
||||||
|
if is_valid:
|
||||||
|
is_valid = self.email_is_valid()
|
||||||
|
if is_valid:
|
||||||
|
is_valid = self.occupazione_is_valid()
|
||||||
|
if is_valid:
|
||||||
|
is_valid = self.fonte_is_valid()
|
||||||
|
if is_valid:
|
||||||
|
is_valid = self.data_firma_is_valid()
|
||||||
|
# Non si puo' convalidare la firma che e'
|
||||||
|
# una immagine
|
||||||
|
return is_valid
|
||||||
|
|
||||||
|
def cognome_is_valid(self):
|
||||||
|
is_valid = True
|
||||||
|
if len(str.rstrip(self.cognome.get())) == 0:
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Cognome' e' obbligatorio"
|
||||||
|
tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio)
|
||||||
|
return is_valid
|
||||||
|
|
||||||
|
def nome_is_valid(self):
|
||||||
|
is_valid = True
|
||||||
|
messaggio = ""
|
||||||
|
if len(str.rstrip(self.nome.get())) == 0:
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Nome' e' obbligatorio"
|
||||||
|
tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio)
|
||||||
|
return is_valid
|
||||||
|
|
||||||
|
def data_nascita_is_valid(self):
|
||||||
|
is_valid = True
|
||||||
|
messaggio = ""
|
||||||
|
if self.data_nascita.get() is None:
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Data di nascita' e' obbligatorio"
|
||||||
|
elif len(str.rstrip(self.data_nascita.get())) == 0:
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Data di nascita' e' obbligatorio"
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
datetime.strptime(self.data_nascita.get(), '%x')
|
||||||
|
except (ValueError):
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Data di nascita' non valida"
|
||||||
|
if not is_valid:
|
||||||
|
tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio)
|
||||||
|
return is_valid
|
||||||
|
|
||||||
|
def comune_nascita_is_valid(self):
|
||||||
|
is_valid = True
|
||||||
|
messaggio = ""
|
||||||
|
if len(str.rstrip(self.comune_nascita.get())) == 0:
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Comune di nascita' e' obbligatorio"
|
||||||
|
tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio)
|
||||||
|
return is_valid, messaggio
|
||||||
|
|
||||||
|
def email_is_valid(self):
|
||||||
|
is_valid = True
|
||||||
|
messaggio = ""
|
||||||
|
if len(str.rstrip(self.email.get())) == 0:
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'E-mail' e' obbligatorio"
|
||||||
|
tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio)
|
||||||
|
return is_valid, messaggio
|
||||||
|
|
||||||
|
def occupazione_is_valid(self):
|
||||||
|
# Non obbligatorio
|
||||||
|
return True
|
||||||
|
|
||||||
|
def fonte_is_valid(self):
|
||||||
|
# Non obbligatorio
|
||||||
|
return True
|
||||||
|
|
||||||
|
def data_firma_is_valid(self):
|
||||||
|
is_valid = True
|
||||||
|
messaggio = ""
|
||||||
|
if self.data_nascita.get() is None:
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Data firma' e' obbligatorio"
|
||||||
|
elif len(str.rstrip(self.data_nascita.get())) == 0:
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Data firma' e' obbligatorio"
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
datetime.strptime(self.data_nascita.get(), '%x')
|
||||||
|
except (ValueError):
|
||||||
|
is_valid = False
|
||||||
|
messaggio = "'Data firma' non valida"
|
||||||
|
if not is_valid:
|
||||||
|
tkMessagebox.showerror(MESSAGE_BOX_TITLE, messaggio)
|
||||||
|
return is_valid
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
|
||||||
|
from iscritti import Iscritti
|
||||||
|
|
||||||
|
def main():
|
||||||
|
root = tk.Tk()
|
||||||
|
Iscritti(root)
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -0,0 +1,102 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# https://www.mmxforge.net/index.php/sviluppo/python/item/9-lettura-dei-dati-della-tessera-sanitaria-con-python
|
||||||
|
|
||||||
|
from smartcard.System import readers
|
||||||
|
import array
|
||||||
|
import string
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
r = readers()
|
||||||
|
reader = r[0]
|
||||||
|
print("Sto usando: {}".format(reader.name))
|
||||||
|
connection = reader.createConnection()
|
||||||
|
connection.connect()
|
||||||
|
|
||||||
|
#Seleziona del MF
|
||||||
|
#CLS 00, istruzione A4 (seleziona file), P1 = P2 = 0 (seleziona per ID),
|
||||||
|
#Lc: 2, Data: 3F00 (id del MF)
|
||||||
|
SELECT_MF = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00]
|
||||||
|
data, sw1, sw2 = connection.transmit(SELECT_MF)
|
||||||
|
#se tutto è andato a buon fine sw1 e sw2 contengono
|
||||||
|
#rispettivamente i valori 0x90 e 0x00 il corrispettivo del 200 in HTTP
|
||||||
|
|
||||||
|
#Seleziona del DF1...vedi sopra
|
||||||
|
SELECT_DF1 = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x11, 0x00]
|
||||||
|
data, sw1, sw2 = connection.transmit(SELECT_DF1)
|
||||||
|
|
||||||
|
#Seleziona del file EF.Dati_personali... vedi sopra sopra
|
||||||
|
SELECT_EF_PERS = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x11, 0x02]
|
||||||
|
data, sw1, sw2 = connection.transmit(SELECT_EF_PERS)
|
||||||
|
|
||||||
|
#leggiamo i dati
|
||||||
|
#CLS 00, istruzione B0 (leggi i dati binari contenuti nel file
|
||||||
|
READ_BIN = [0x00, 0xB0, 0x00, 0x00, 0x00, 0x00]
|
||||||
|
data, sw1, sw2 = connection.transmit(READ_BIN)
|
||||||
|
#data contiene i dati anagrafici in formato binario
|
||||||
|
#trasformiamo il tutto in una stringa
|
||||||
|
stringa_dati_personali = array.array('B', data).tostring()
|
||||||
|
|
||||||
|
dimensione = int(stringa_dati_personali[0:6],16)
|
||||||
|
print("Dimensione in byte dei dati: {}".format( dimensione))
|
||||||
|
numero_field = 0
|
||||||
|
|
||||||
|
prox_field_size = int(stringa_dati_personali[6:8], 16)
|
||||||
|
da = 8
|
||||||
|
a = da + prox_field_size
|
||||||
|
if prox_field_size > 0:
|
||||||
|
codice_emettitore = stringa_dati_personali[da:a]
|
||||||
|
print("Codice emettitore: {}".format(codice_emettitore.decode("utf-8")))
|
||||||
|
numero_field += 1
|
||||||
|
|
||||||
|
da = a
|
||||||
|
a +=2
|
||||||
|
prox_field_size = int(stringa_dati_personali[da:a], 16)
|
||||||
|
da=a
|
||||||
|
a += prox_field_size
|
||||||
|
if prox_field_size > 0:
|
||||||
|
data_rilascio_tessera = stringa_dati_personali[da:a]
|
||||||
|
year = int(data_rilascio_tessera[-4:])
|
||||||
|
month = int(data_rilascio_tessera[2:4])
|
||||||
|
day = int(data_rilascio_tessera[0:2])
|
||||||
|
dtm_data_rliascio = datetime.date(year, month, day)
|
||||||
|
# print(string("Data rilascio tessera: ", data_rilascio_tessera[0:2], "/", data_rilascio_tessera[2:4], "/", data_rilascio_tessera[-4:])
|
||||||
|
print("Data rilascio tessera: {}".format(dtm_data_rliascio))
|
||||||
|
numero_field += 1
|
||||||
|
|
||||||
|
da = a
|
||||||
|
a +=2
|
||||||
|
prox_field_size = int(stringa_dati_personali[da:a], 16)
|
||||||
|
da=a
|
||||||
|
a += prox_field_size
|
||||||
|
if prox_field_size > 0:
|
||||||
|
data_scadenza_tessera = stringa_dati_personali[da:a]
|
||||||
|
year = int(data_scadenza_tessera[-4:])
|
||||||
|
month = int(data_scadenza_tessera[2:4])
|
||||||
|
day = int(data_scadenza_tessera[0:2])
|
||||||
|
dtm_data_scadenza = datetime.date(year, month, day)
|
||||||
|
# print("Data scadenza tessera: ", data_scadenza_tessera[0:2]+"/"+data_scadenza_tessera[2:4]+"/"+data_scadenza_tessera[-4:])
|
||||||
|
print("Data scadenza tessera: {}".format(dtm_data_scadenza))
|
||||||
|
numero_field += 1
|
||||||
|
|
||||||
|
# da = a
|
||||||
|
# a +=2
|
||||||
|
# prox_field_size = int(stringa_dati_personali[da:a], 16)
|
||||||
|
# da=a
|
||||||
|
# a += prox_field_size
|
||||||
|
# if prox_field_size > 0:
|
||||||
|
# cognome = stringa_dati_personali[da:a]
|
||||||
|
# print("Cognome: {}".format(cognome))
|
||||||
|
# numero_field += 1
|
||||||
|
|
||||||
|
while a < dimensione:
|
||||||
|
da = a
|
||||||
|
a += 2
|
||||||
|
prox_field_size = int(stringa_dati_personali[da:a], 16)
|
||||||
|
da=a
|
||||||
|
a += prox_field_size
|
||||||
|
if prox_field_size > 0:
|
||||||
|
field = stringa_dati_personali[da:a]
|
||||||
|
print("Field#{}: {}".format(numero_field, field))
|
||||||
|
numero_field += 1
|
|
@ -0,0 +1,6 @@
|
||||||
|
Pillow==6.0.0
|
||||||
|
# Su Linux installare
|
||||||
|
# sudo apt install pcscd
|
||||||
|
# sudo apt install pcsc-tools
|
||||||
|
# sudo apt install python3-pyscard
|
||||||
|
# pyscard==1.9.8
|
Loading…
Reference in New Issue