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