Skip to content

Commit 92bce8e

Browse files
committed
fix bugs, enhance SQLiteDatabaseCreator
1 parent 41ac134 commit 92bce8e

File tree

7 files changed

+137
-35
lines changed

7 files changed

+137
-35
lines changed

core/database.py

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@
55
import shutil
66
import sqlite3
77
import hashlib
8-
from .datatype import DynamicObject
9-
from typing import Any, Optional, Union, List, Tuple, Sequence
8+
from .datatype import DynamicObject, str2float, str2number
9+
from typing import Any, Optional, Union, List, Tuple, Sequence, Dict
10+
from ..misc.settings import UiInputSetting, UiIntegerInput, UiDoubleInput
1011

1112
try:
1213
from pysqlcipher3 import dbapi2 as sqlcipher
1314
except ImportError:
1415
import sqlite3 as sqlcipher
1516

1617
__all__ = ['SQLiteDatabase', 'SQLCipherDatabase', 'SQLiteUserPasswordDatabase', 'SQLiteDatabaseError',
17-
'SQLiteDatabaseCreator', 'SQLiteGeneralSettingsItem']
18+
'SQLiteDatabaseCreator', 'SQLiteGeneralSettingsItem', 'SQLiteUIElementScheme']
1819

1920

2021
class SQLiteDatabaseError(Exception):
@@ -575,6 +576,25 @@ def __repr__(self):
575576
return ", ".join(data)
576577

577578

579+
class SQLiteUIElementScheme(DynamicObject):
580+
_properties = {'name', 'min', 'max', 'precision'}
581+
_check = {
582+
'name': lambda x: isinstance(x, str),
583+
'min': lambda x: isinstance(x, (int, float)),
584+
'nax': lambda x: isinstance(x, (int, float)),
585+
'precision': lambda x: isinstance(x, int)
586+
}
587+
588+
def getNumericalInput(self, name: str = '', readonly: bool = False) -> UiInputSetting:
589+
name = name or self.name
590+
if self.precision:
591+
return UiDoubleInput(
592+
name=name, minimum=self.min, maximum=self.max, decimals=self.precision, readonly=readonly
593+
)
594+
else:
595+
return UiIntegerInput(name=name, minimum=self.min, maximum=self.max, readonly=readonly)
596+
597+
578598
class SQLiteDatabaseCreator(object):
579599
DESC_ID = -5
580600
NAME_ID = -4
@@ -634,6 +654,57 @@ def __output_protobuf_items(fp, name: str, items: List[str]):
634654
fp.write(";\n".join(items))
635655
fp.write(";\n}\n/*")
636656

657+
@staticmethod
658+
def get_settings_table_ui_scheme(name: str) -> Dict[int, SQLiteUIElementScheme]:
659+
try:
660+
db = SQLiteDatabase(os.path.join('config', 'gas_sampler.db'))
661+
except (OSError, SQLiteDatabaseError) as e:
662+
print("Load db scheme error: {}".format(e))
663+
return dict()
664+
665+
scheme = dict()
666+
for item in db.getTableData(name):
667+
idx, name, data, min_, max_, precision, desc = item
668+
precision = str2number(precision)
669+
process = str2float if precision else str2number
670+
scheme[idx] = SQLiteUIElementScheme(name=name, data=process(data),
671+
precision=precision, min=process(min_), max=process(max_))
672+
673+
return scheme
674+
675+
@staticmethod
676+
def get_general_table_ui_scheme(name: str) -> Dict[int, SQLiteUIElementScheme]:
677+
try:
678+
db = SQLiteDatabase(os.path.join('config', 'gas_sampler.db'))
679+
except (OSError, SQLiteDatabaseError) as e:
680+
print("Load db scheme error: {}".format(e))
681+
return dict()
682+
683+
names = db.selectRecord(
684+
name, condition=SQLiteDatabase.conditionFormat('id', SQLiteDatabaseCreator.NAME_ID)
685+
)[0][1: -1]
686+
687+
lowers = db.selectRecord(
688+
name, condition=SQLiteDatabase.conditionFormat('id', SQLiteDatabaseCreator.LOWER_LIMIT_ID)
689+
)[0][1: -1]
690+
691+
uppers = db.selectRecord(
692+
name, condition=SQLiteDatabase.conditionFormat('id', SQLiteDatabaseCreator.UPPER_LIMIT_ID)
693+
)[0][1: -1]
694+
695+
precisions = db.selectRecord(
696+
name, condition=SQLiteDatabase.conditionFormat('id', SQLiteDatabaseCreator.PRECISION_ID)
697+
)[0][1: -1]
698+
699+
scheme = dict()
700+
for idx in range(len(names)):
701+
precision = str2number(precisions[idx])
702+
process = str2float if precision else str2number
703+
scheme[idx] = SQLiteUIElementScheme(name=names[idx], precision=precision,
704+
min=process(lowers[idx]), max=process(uppers[idx]))
705+
706+
return scheme
707+
637708
@property
638709
def database_path(self) -> str:
639710
return self._db_name

core/uimailbox.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,11 @@ def send(self, mail: BaseUiMail) -> bool:
177177
if not isinstance(mail, BaseUiMail):
178178
return False
179179

180-
self.hasNewMail.emit(mail)
181-
return True
180+
try:
181+
self.hasNewMail.emit(mail)
182+
return True
183+
except RuntimeError:
184+
return False
182185

183186
@Slot(object)
184187
def mailProcess(self, mail: BaseUiMail) -> bool:

dashboard/input.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ def showKeyboard(self):
316316
self.setText(str(value))
317317
self.numberChanged.emit(value)
318318

319+
def setValue(self, value: Union[int, float]):
320+
number_decimals = self.property('decimals')
321+
self.setText("{0:.{1}f}".format(value, number_decimals) if number_decimals else str(value))
322+
319323
def mousePressEvent(self, ev):
320324
if ev.button() != Qt.LeftButton:
321325
return

gui/container.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ def __init__(self, layout: QLayout, parent: Optional[QWidget] = None):
231231
self.__initSignalAndSlots()
232232

233233
def __initSignalAndSlots(self):
234+
from ..dashboard.input import VirtualNumberInput
235+
234236
for component in self.getAll():
235237
if isinstance(component, QSpinBox):
236238
component.valueChanged.connect(self.slotDataChanged)
@@ -242,6 +244,8 @@ def __initSignalAndSlots(self):
242244
component.stateChanged.connect(self.slotDataChanged)
243245
elif isinstance(component, QRadioButton):
244246
component.clicked.connect(self.slotDataChanged)
247+
elif isinstance(component, VirtualNumberInput):
248+
component.numberChanged.connect(self.slotDataChanged)
245249
elif isinstance(component, QLineEdit):
246250
component.textChanged.connect(self.slotDataChanged)
247251
elif isinstance(component, QTextEdit):
@@ -295,6 +299,8 @@ def getComponentData(component: QWidget) -> Any:
295299

296300
@staticmethod
297301
def setComponentData(component: QWidget, data: Any):
302+
from ..dashboard.input import VirtualNumberInput
303+
298304
if isinstance(component, QSpinBox):
299305
component.setValue(str2number(data))
300306
elif isinstance(component, QDoubleSpinBox):
@@ -315,9 +321,10 @@ def setComponentData(component: QWidget, data: Any):
315321
elif isinstance(component, QRadioButton):
316322
component.setCheckable(True)
317323
component.setChecked(str2number(data))
324+
elif isinstance(component, VirtualNumberInput):
325+
component.setValue(data)
318326
elif isinstance(component, QLineEdit):
319-
if isinstance(data, str):
320-
component.setText(data)
327+
component.setText(str(data))
321328
elif isinstance(component, QTextEdit):
322329
if isinstance(data, str):
323330
component.setText(data)

gui/view.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ def getItemData(self, row: int, column: int, role: Qt.ItemDataRole = Qt.DisplayR
343343
else:
344344
return model.itemData(model.index(row, column, QModelIndex())).get(role)
345345

346-
def setItemData(self, row: int, column: int, data: List[Any], role: Qt.ItemDataRole = Qt.EditRole):
346+
def setItemData(self, row: int, column: int, data: Any, role: Qt.ItemDataRole = Qt.EditRole):
347347
model = self.model()
348348
if not isinstance(model, QAbstractItemModel):
349349
return False

gui/widget.py

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@
3232
from PySide.QtGui import *
3333
from PySide.QtCore import *
3434
from datetime import datetime
35-
from threading import Thread
3635

3736
from .container import ComponentManager
3837
from ..dashboard.input import VirtualNumberInput
3938
from ..misc.windpi import get_program_scale_factor
4039
from .misc import SerialPortSelector, NetworkInterfaceSelector
4140
from ..core.datatype import str2number, str2float, DynamicObject, DynamicObjectDecodeError
42-
from ..misc.settings import UiInputSetting, UiLogMessage, UiLayout, UiFontInput, UiColorInput
41+
from ..misc.settings import UiInputSetting, UiLogMessage, UiLayout, UiFontInput, UiColorInput, \
42+
UiDoubleInput, UiIntegerInput, UiTextInput, UiFileInput
4343

4444

4545
__all__ = ['BasicWidget', 'PaintWidget',
@@ -2287,10 +2287,12 @@ def __initUi(self):
22872287
# QLine edit special process re check
22882288
if isinstance(widget, QLineEdit):
22892289
widget.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
2290-
widget.textChanged.connect(self.slotSettingChanged)
2291-
widget.textChanged.connect(
2292-
lambda x: self.settingChangedDetail.emit(widget.property("data"), x)
2293-
)
2290+
2291+
if not isinstance(widget, VirtualNumberInput):
2292+
widget.textChanged.connect(self.slotSettingChanged)
2293+
widget.textChanged.connect(
2294+
lambda x: self.settingChangedDetail.emit(widget.property("data"), x)
2295+
)
22942296
elif isinstance(widget, QLayout):
22952297
# Add label and layout
22962298
layout.addWidget(QLabel(self.tr(ui_input.get_name())), row, column)
@@ -2518,14 +2520,15 @@ def createInputWidget(setting, name=None, parent=None):
25182520
if setting.is_int_type():
25192521
if setting.is_readonly():
25202522
widget = VirtualNumberInput(parent=parent, initial_value=setting.get_data(),
2521-
min_=setting.get_check()[0], max_=setting.get_check()[1])
2523+
min_=setting.get_check()[UiIntegerInput.CHECK.MIN],
2524+
max_=setting.get_check()[UiIntegerInput.CHECK.MAX])
25222525
widget.setProperty("format", "int")
25232526
else:
25242527
widget = QSpinBox(parent)
2525-
widget.setMinimum(setting.get_check()[0])
2526-
widget.setMaximum(setting.get_check()[1])
2528+
widget.setMinimum(setting.get_check()[UiIntegerInput.CHECK.MIN])
2529+
widget.setMaximum(setting.get_check()[UiIntegerInput.CHECK.MAX])
25272530
widget.setValue(setting.get_data())
2528-
widget.setSingleStep(setting.get_check()[2])
2531+
widget.setSingleStep(setting.get_check()[UiIntegerInput.CHECK.STEP])
25292532
elif setting.is_bool_type():
25302533
widget = QCheckBox(parent=parent)
25312534
widget.setCheckable(True)
@@ -2535,25 +2538,25 @@ def createInputWidget(setting, name=None, parent=None):
25352538
widget.setText(setting.get_data())
25362539
widget.setPlaceholderText(setting.get_default())
25372540
# Set regular expression and max length
2538-
widget.setProperty("filter", setting.check[0])
2539-
widget.setValidator(QRegExpValidator(QRegExp(setting.check[0])))
2540-
widget.setMaxLength(setting.check[1])
2541+
widget.setProperty("filter", setting.check[UiTextInput.CHECK.REGEXP])
2542+
widget.setValidator(QRegExpValidator(QRegExp(setting.check[UiTextInput.CHECK.REGEXP])))
2543+
widget.setMaxLength(setting.check[UiTextInput.CHECK.LENGTH])
25412544
elif setting.is_float_type():
25422545
if setting.is_readonly():
25432546
widget = VirtualNumberInput(parent=parent,
25442547
initial_value=setting.get_data(),
2545-
min_=setting.get_check()[0],
2546-
max_=setting.get_check()[1],
2547-
decimals=setting.get_check()[2])
2548+
min_=setting.get_check()[UiDoubleInput.CHECK.MIN],
2549+
max_=setting.get_check()[UiDoubleInput.CHECK.MAX],
2550+
decimals=setting.get_check()[UiDoubleInput.CHECK.DECIMALS])
25482551
widget.setProperty("format", "float")
25492552
else:
25502553
widget = QDoubleSpinBox(parent)
2551-
widget.setMinimum(setting.get_check()[0])
2552-
widget.setMaximum(setting.get_check()[1])
2554+
widget.setMinimum(setting.get_check()[UiDoubleInput.CHECK.MIN])
2555+
widget.setMaximum(setting.get_check()[UiDoubleInput.CHECK.MAX])
25532556
widget.setValue(setting.get_data())
2554-
widget.setSingleStep(setting.get_check()[2])
2555-
if len(setting.get_check()) > 3:
2556-
widget.setDecimals(setting.get_check()[3])
2557+
widget.setSingleStep(setting.get_check()[UiDoubleInput.CHECK.STEP])
2558+
if len(setting.get_check()) > UiDoubleInput.CHECK.DECIMALS:
2559+
widget.setDecimals(setting.get_check()[UiDoubleInput.CHECK.DECIMALS])
25572560
elif setting.is_select_type():
25582561
widget = QComboBox(parent)
25592562
widget.addItems(setting.get_check())
@@ -2625,14 +2628,14 @@ def createInputWidget(setting, name=None, parent=None):
26252628
"Enable", None,
26262629
QApplication.UnicodeUTF8), parent=parent)
26272630
enable.setProperty("data", JsonSettingWidget.get_file_input_enable_key(name))
2628-
enable.setVisible(setting.get_check()[-1] == str(True))
2631+
enable.setVisible(setting.get_check()[UiFileInput.CHECK_SELECTABLE] == str(True))
26292632

26302633
button = QPushButton(QApplication.translate("JsonSettingWidget",
26312634
"Please Select File",
26322635
None, QApplication.UnicodeUTF8), parent=parent)
26332636
button.setProperty("clicked", "file")
26342637
button.setProperty("title", setting.get_name())
2635-
button.setProperty("private", setting.get_check()[:-1])
2638+
button.setProperty("private", setting.get_check()[:UiFileInput.CHECK_SELECTABLE])
26362639

26372640
layout = QHBoxLayout()
26382641
layout.addWidget(enable)

misc/settings.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import json
55
import codecs
66
import logging
7+
import collections
78
from string import Template
89
from typing import Tuple, Optional, Any, Sequence, Union, List, TypeVar, NamedTuple
910

@@ -66,10 +67,15 @@ def store(cls, settings: DynamicObject, path: Optional[str] = None):
6667
return False
6768

6869
path = path if path else cls._default_path
69-
if not os.path.isdir(os.path.dirname(path)) and len(os.path.dirname(path)):
70-
os.makedirs(os.path.dirname(path))
71-
with codecs.open(path, "w", "utf-8") as fp:
72-
json.dump(settings.dict, fp, indent=4, ensure_ascii=False)
70+
71+
try:
72+
if not os.path.isdir(os.path.dirname(path)) and len(os.path.dirname(path)):
73+
os.makedirs(os.path.dirname(path))
74+
with codecs.open(path, "w", "utf-8") as fp:
75+
json.dump(settings.dict, fp, indent=4, ensure_ascii=False)
76+
except OSError as e:
77+
print("store settings to {}, failed: {}".format(path, e))
78+
return False
7379

7480
return True
7581

@@ -246,6 +252,8 @@ def getDemoSettings(d2: bool = False) -> DynamicObject:
246252

247253

248254
class UiFileInput(UiInputSetting):
255+
CHECK_SELECTABLE = -1
256+
249257
def __init__(self, name: str, fmt: Tuple[str, ...], default: str = "", selectable: bool = False):
250258
fmt = list(fmt)
251259
fmt.append(str(selectable))
@@ -294,6 +302,8 @@ def get_stylesheet(font_setting: str) -> str:
294302

295303

296304
class UiTextInput(UiInputSetting):
305+
CHECK = collections.namedtuple('UiTextInputCheck', ['REGEXP', 'LENGTH'])(*range(2))
306+
297307
def __init__(self, name: str, length: int, default: str = "", re_: str = "[\s\S]*", readonly: bool = False):
298308
super(UiTextInput, self).__init__(name=name, data=default, default=default,
299309
check=(re_, length), readonly=readonly, type="TEXT")
@@ -406,13 +416,17 @@ def get_bg_color_stylesheet(color_setting: str, border: bool = False) -> str:
406416

407417

408418
class UiDoubleInput(UiInputSetting):
419+
CHECK = collections.namedtuple('UiDoubleInputCheck', ['MIN', 'MAX', 'STEP', 'DECIMALS'])(*range(4))
420+
409421
def __init__(self, name: str, minimum: float, maximum: float,
410422
default: float = 0.0, step: float = 1.0, decimals: int = 1, readonly: bool = False):
411423
super(UiDoubleInput, self).__init__(name=name, data=default, default=default,
412424
check=(minimum, maximum, step, decimals), readonly=readonly, type="FLOAT")
413425

414426

415427
class UiIntegerInput(UiInputSetting):
428+
CHECK = collections.namedtuple('UiIntegerInputCheck', ['MIN', 'MAX', 'STEP'])(*range(3))
429+
416430
def __init__(self, name: str, minimum: int, maximum: int, default: int = 0, step: int = 1, readonly: bool = False):
417431
super(UiIntegerInput, self).__init__(name=name, data=default, default=default,
418432
check=(minimum, maximum, step), readonly=readonly, type="INT")

0 commit comments

Comments
 (0)