presik_pos/app/commons/forms.py

500 lines
15 KiB
Python

import locale
from PyQt5.QtWidgets import (
QLineEdit, QLabel, QComboBox, QGridLayout, QTextEdit, QTreeView,
QCompleter, QCalendarWidget, QCheckBox
)
from PyQt5.QtCore import Qt, QRegExp
from PyQt5.QtGui import QRegExpValidator, QDoubleValidator
from .qt_models import get_simple_model
regex_ = QRegExp("^\\d{1,3}(([.]\\d{3})*),(\\d{2})$")
validator = QRegExpValidator(regex_)
try:
locale.setlocale(locale.LC_ALL, str('es_CO.UTF-8'))
except:
print("Warning: Error setting locale")
__all__ = ['Label', 'Field', 'ComboBox', 'GridForm', 'FieldMoney', 'FieldDate', 'CheckBox']
def set_object_name(obj, type_, value):
size = 'small'
color = 'gray'
if value.get('size'):
size = value.get('size')
if value.get('color'):
color = value.get('color')
name = type_ + size + '_' + color
if value.get('font_size'):
font_size = value.get('font_size')
name += '_' + font_size
obj.setObjectName(name)
class Completer(QCompleter):
def __init__(self, parent, records, fields):
super(Completer, self).__init__()
self.parent = parent
self.treeview_search = QTreeView()
col_headers = self.treeview_search.header()
col_headers.hide()
self.setPopup(self.treeview_search)
self.fields = fields
self._set_model(records, fields)
self.activated.connect(self.on_accept)
self.setFilterMode(Qt.MatchContains)
self.setCaseSensitivity(Qt.CaseInsensitive)
self.setWrapAround(True)
self.setCompletionColumn(1)
self.treeview_search.setColumnWidth(1, 300)
self.treeview_search.setColumnHidden(0, True)
self.id = None
def get_values(self, records):
vkeys = [f[0] for f in self.fields]
values = []
for r in records:
row = []
for key in vkeys:
if '.' in key:
attrs = key.split('.')
val = r.copy()
for a in attrs:
val = val[a]
else:
val = r[key]
row.append(val)
values.append(row)
return values
def _set_model(self, records, headers):
headers = [f[1] for f in self.fields]
values = self.get_values(records)
self.model = get_simple_model(self.parent, values, headers)
self.setModel(self.model)
def on_accept(self):
model_index = self._get_model_index()
idx = self.model.index(model_index.row(), 0)
self.id = idx.data()
def _get_model_index(self):
item_view = self.popup()
index = item_view.currentIndex()
proxy_model = self.completionModel()
model_index = proxy_model.mapToSource(index)
return model_index
class Label(QLabel):
def __init__(self, obj, key, value, align='right', form=None):
super(Label, self).__init__()
self.key = key
self.setText(value['name'] + ':')
set_object_name(self, 'label_', value)
if align == 'left':
self.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
else:
self.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
class Field(QLineEdit):
def __init__(self, obj, key, value, type=None, form=None):
super(Field, self).__init__()
setattr(obj, 'field_' + key, self)
self.key = key
self.form = form
self.parent = obj
set_object_name(self, 'field_', value)
if value.get('type') == 'numeric':
self.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
elif value.get('type') == 'relation':
self.set_completer(
value.get('model'), value.get('fields'), value.get('domain')
)
def set_completer(self, model, fields, domain=[]):
records = model.find(domain)
self.completer = Completer(self.parent, records, fields)
self.setCompleter(self.completer)
def get_id(self):
return self.completer.id
class TextField(QTextEdit):
def __init__(self, obj, key, value, form=None):
super(TextField, self).__init__()
self.key = key
self.form = form
setattr(obj, 'field_' + key, self)
set_object_name(self, 'field_', value)
self.value_changed = False
def textChanged(self, text):
self.value_changed = True
class CheckBox(QCheckBox):
def __init__(self, obj, key, value, form=None):
super(CheckBox, self).__init__()
self.key = key
self.form = form
setattr(obj, 'field_' + key, self)
self.setText(value.get('name'))
class FieldDate(QCalendarWidget):
def __init__(self, obj, key, value, form=None):
super(FieldDate, self).__init__()
self.key = key
self.form = form
setattr(obj, 'field_' + key, self)
set_object_name(self, 'field_', value)
self.value_changed = False
self.key = key
self.selectionChanged.connect(self.on_change)
if value.get('default'):
self.form.setState(self.key, value.get('default'))
def get_value(self):
return self.selectedDate().toPyDate()
def on_change(self):
date_ = self.selectedDate().toPyDate()
if self.form:
self.form.setState(self.key, date_)
class FieldMoney(QLineEdit):
def __init__(self, obj, key, value={}, amount=None, digits=2, readonly=True, form=None):
super(FieldMoney, self).__init__()
setattr(obj, 'field_' + key, self)
self.key = key
self.form = form
set_object_name(self, 'field_', value)
self.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.digits = 0
self.value_changed = False
self.textEdited.connect(self.value_edited)
self._text = '0'
self.amount = 0
self.setReadOnly(readonly)
validator = QDoubleValidator()
validator.setDecimals(2)
self.setValidator(validator)
if not amount:
self.zero()
def __str__(self):
return self.format_text()
def format_text(self, text_):
amount = float(text_)
return format(round(amount), ',d')
def setText(self, amount):
if not amount:
text = ''
else:
text = self.format_text(amount)
super(FieldMoney, self).setText(str(text))
def zero(self):
self.setText(str(0))
def value_edited(self, amount):
self.value_changed = True
def show(self):
pass
def get_value(self):
return self.text()
class FieldInput(QLineEdit):
def __init__(self, obj, key, value, amount=None, digits=2, form=None):
super(FieldInput, self).__init__()
setattr(obj, 'field_' + key, self)
self.key = key
self.form = form
self.parent = obj
set_object_name(self, 'field_', value)
self.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.digits = 0
self.field = key
self.value_changed = False
self.textEdited.connect(self.value_edited)
self.editingFinished.connect(self.finished_edit)
self._text = '0'
self.amount = 0
validator = QDoubleValidator()
validator.setDecimals(2)
self.setValidator(validator)
self.method_on_change = getattr(obj, 'on_change_input')
if not amount:
self.zero()
def __str__(self):
return self.format_text()
def format_text(self, text_):
amount = float(text_)
return format(round(amount), ',d')
def setText(self, amount):
if not amount:
text = ''
else:
text = self.format_text(amount)
super(FieldInput, self).setText(str(text))
def zero(self):
self.setText(str(0))
def value_edited(self, amount):
self.value_changed = True
def finished_edit(self):
amount = self.text()
self.method_on_change(self.field, amount)
def show(self):
pass
class FieldNumeric(QLineEdit):
def __init__(self, obj, key, value, amount=None, digits=2, readonly=True, form=None):
super(FieldNumeric, self).__init__()
setattr(obj, 'field_' + key, self)
set_object_name(self, 'field_', value)
self.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.key = key
self.form = form
self.digits = 2
self.value_changed = False
self.textEdited.connect(self.value_edited)
self._text = '0'
self.amount = 0
self.setReadOnly(readonly)
validator = QDoubleValidator()
validator.setDecimals(2)
self.setValidator(validator)
if not amount:
self.zero()
def __str__(self):
return self.format_text()
# def format_text(self, text_):
# amount = float(text_)
# return "{:,}".format(round(amount, self.digits))
def setText(self, amount):
if not amount:
text = ''
else:
text = amount
text = str(text).lstrip('0')
super(FieldNumeric, self).setText(text)
def zero(self):
self.setText(str(0))
def value_edited(self, amount):
self.value_changed = True
def show(self):
pass
def get_value(self):
pass
class ComboBox(QComboBox):
def __init__(self, obj, key, data, form=None):
super(ComboBox, self).__init__()
setattr(obj, 'field_' + key, self)
self.parent = obj
self.key = key
self.form = form
self.setFrame(True)
self.setObjectName('field_' + key)
values = []
if data.get('values'):
values = data.get('values')
heads = []
if data.get('heads'):
heads = data.get('heads')
selection_model = get_simple_model(obj, values, heads)
self.setModel(selection_model)
self.setModelColumn(1)
selection_model.findItems(str(3), column=0)
self.method_on_change = None
self.currentIndexChanged.connect(self.on_change)
if data.get('default'):
self.set_from_id(data['default']['id'])
if self.form:
self.form.setState(self.key, self.get_id())
if data.get('on_change'):
self.method_on_change = getattr(self.parent, data.get('on_change'))
def on_change(self, index):
if self.method_on_change:
self.method_on_change(index)
if self.form:
self.form.setState(self.key, self.get_id())
def set_editable(self, value=True):
self.setEditable(value)
def set_enabled(self, value=True):
self.setEnabled(value)
def get_value(self):
return self.get_id()
def get_id(self):
model = self.model()
row = self.currentIndex()
column = 0 # id ever is column Zero
res = model.item(row, column)
return res.text()
def get_label(self):
model = self.model()
row = self.currentIndex()
column = 1 # id ever is column Zero
res = model.item(row, column)
return res.text()
def set_from_id(self, id_):
model = self.model()
items = model.findItems(str(id_), column=0)
idx = model.indexFromItem(items[0])
self.setCurrentIndex(idx.row())
def set_none(self):
self.setCurrentIndex(0)
class GridForm(QGridLayout):
"""
Add a simple form Grid Style to screen,
from a data dict with set of {values, attributes}
example:
(field_name, {
'name': string descriptor,
'readonly': Bool,
'type': type_widget,
'placeholder': True or False,
'values': ['a', 'b',...] - optional
}),
col:: is number of columns
type_widget :: field or selection
"""
def __init__(self, obj, values, col=1, size=None):
super(GridForm, self).__init__()
self.setSpacing(5)
if size != 'large':
self.setSpacing(3)
row = 1
cols = 0
align = 'right'
self.store = {}
if col == 0:
align = 'left'
setted_focus = False
for key, value in values.items():
expand = 0
self.store[key] = None
if value.get('editable'):
_field = FieldInput(obj, key, value, form=self)
elif value.get('type') == 'selection':
_field = ComboBox(obj, key, value, form=self)
elif value.get('type') == 'checkbox':
_field = CheckBox(obj, key, value, form=self)
expand = 1
elif value.get('type') == 'money':
_field = FieldMoney(obj, key, value, form=self)
elif value.get('type') == 'text':
_field = TextField(obj, key, value, form=self)
setted_focus = True
expand = 1
align = 'left'
elif value.get('type') == 'date':
_field = FieldDate(obj, key, value, form=self)
else:
_field = Field(obj, key, value, self)
if value.get('password') is True:
_field.setEchoMode(QLineEdit.Password)
if value.get('placeholder'):
_field.setPlaceholderText(value['name'])
if not setted_focus:
self.focus_widget = _field
setted_focus = True
if not value.get('placeholder'):
_label = Label(obj, key, value, align, form=self)
setattr(self, 'label_' + key, _label)
self.setRowStretch(row, 0)
column1 = cols * col + 1 + expand
column2 = column1 + 1 + expand
if value.get('invisible') is True:
continue
if not expand:
if not value.get('placeholder'):
self.addWidget(_label, row, column1)
if col == 0:
row = row + 1
self.addWidget(_field, row, column1)
else:
self.addWidget(_field, row, column2)
else:
self.addWidget(_label, row, column1, row, column2)
self.addWidget(_field, row + 1, column1, row + 1, column2)
if value.get('translate') is True:
_field.translate = True
if value.get('readonly') is True:
_field.setReadOnly(True)
_field.setFocusPolicy(Qt.NoFocus)
if cols < (col - 1):
cols += 1
else:
row += 1
cols = 0
def setState(self, key, value):
print('ingresa a setstate', key, value)
self.store[key] = value
def getStore(self):
return self.store
def set_focus(self):
if hasattr(self, 'focus_widget'):
self.focus_widget.setFocus()