500 lines
15 KiB
Python
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()
|