def loadTsContent(self, key: str) -> bool: """ Load the contents of an existing translation file into the disk cache into the translator. The file must be entered in the disk cache before calling this method, in Otherwise, nothing will be done. @param key Sha1 key that identifies the file in the disk cache @return TRUE if the operation was successful """ if self._id_module == "sys": ts_file = filedir("./system_module/translations/%s.%s" % (self._id_module, self._lang)) else: if application.PROJECT.conn_manager is None: raise Exception("Project is not connected yet") ts_file = filedir("%s/cache/%s/%s/file.ts/%s.%s/%s" % ( application.PROJECT.tmpdir, application.PROJECT.conn_manager.useConn("default").DBName(), self._id_module, self._id_module, self._lang, key, )) # qmFile = self.AQ_DISKCACHE_DIRPATH + "/" + key + ".qm" ret_ = False if not self._translation_from_qm: ret_ = self.load_ts("%s.ts" % ts_file) if not ret_: self.logger.warning("For some reason, i cannot load '%s.ts'", ts_file) else: qm_file = "%s.qm" % ts_file if os.path.exists(qm_file): if ts_file in (None, ""): return False else: from . import fltranslations trans = fltranslations.FLTranslations() trans.lrelease("%s.ts" % ts_file, qm_file, not self._multi_lang) ret_ = self.load(qm_file) if not ret_: self.logger.warning("For some reason, i cannot load '%s'", qm_file) return ret_
def __init__( self, parent: Optional[Any] = None, name: str = "FLDataTable", popup: bool = False ): """Inicialize.""" super().__init__(parent) if parent: self._parent = parent self.setObjectName(name) self.readonly_ = False self.editonly_ = False self.insertonly_ = False self.refreshing_ = False self.filter_ = "" self.sort_ = "" self.rowSelected = -1 self.colSelected = -1 self.primarysKeysChecked_ = [] self.persistentFilter_ = "" self.widthCols_ = {} self.pixOk_ = utils_base.filedir("./core/images/icons", "unlock.png") self.pixNo_ = utils_base.filedir("./core/images/icons", "lock.png") self.paintFieldMtd_ = None self.refreshing_ = False self.popup_ = False self.showAllPixmaps_ = False self.onlyTable_ = False self.function_get_color = None self.changingNumRows_ = False self.cursor_ = None self._v_header = self.verticalHeader() self._v_header.setDefaultSectionSize(22) self._h_header = self.horizontalHeader() self._h_header.setDefaultSectionSize(120) self._h_header.setSectionResizeMode(QtWidgets.QHeaderView.Interactive) self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) self.setAlternatingRowColors(True) self.setSortingEnabled(True) self.sortByColumn(0, QtCore.Qt.AscendingOrder) self.fltable_iface = None self.popup_ = popup
def setup_gui(app: QtWidgets.QApplication, options: Values) -> None: """Configure GUI app.""" from pineboolib.core.utils.utils_base import filedir from PyQt5 import QtGui noto_fonts = [ "NotoSans-BoldItalic.ttf", "NotoSans-Bold.ttf", "NotoSans-Italic.ttf", "NotoSans-Regular.ttf", ] for fontfile in noto_fonts: QtGui.QFontDatabase.addApplicationFont( filedir("./core/fonts/Noto_Sans", fontfile)) style_app: str = settings.config.value("application/style", "Fusion") app.setStyle(style_app) # type: ignore default_font = settings.config.value("application/font", None) if default_font is None: font = QtGui.QFont("Noto Sans", 9) font.setBold(False) font.setItalic(False) else: # FIXME: FLSettings.readEntry does not return an array font = QtGui.QFont(default_font[0], int(default_font[1]), int(default_font[2]), default_font[3] == "true") app.setFont(font)
def __init__(self) -> None: """Constructor.""" # self._conn = None self.dgi = None self.tree = None self.root = None self.alternative_folder = None self.apppath = "" self.tmpdir = settings.config.value("ebcomportamiento/temp_dir") self.parser = None # self.main_form_name: Optional[str] = None self.delete_cache = False self.parse_project = False self.translator_ = [] # FIXME: Add proper type self.actions = {} # FIXME: Add proper type self.tables = {} # FIXME: Add proper type self.files = {} # FIXME: Add proper type self.areas = {} self.modules = {} self.options = Values() if self.tmpdir is None: self.tmpdir = utils_base.filedir("%s/Pineboo/tempdata" % Path.home()) settings.config.set_value("ebcomportamiento/temp_dir", self.tmpdir) if not os.path.exists(self.tmpdir): Path(self.tmpdir).mkdir(parents=True, exist_ok=True) self._session_func_ = None self._conn_manager = pnconnectionmanager.PNConnectionManager() self.pending_conversion_list = []
def setCurrent(val: Optional[str] = None) -> None: """ Change current working folder. @param val. Ruta especificada """ os.chdir(val or filedir("."))
def initScript(self) -> None: """Inicialize main script.""" self.createUi( utils_base.filedir("plugins/mainform/eneboo_mdi/mainform.ui")) self.container_ = self self.init()
def load_model(nombre): """Import and return sqlAlchemy model for given table name.""" if nombre is None: return # if nombre in processed_: # return None # processed_.append(nombre) # nombre_qsa = nombre.replace("_model", "") # model_name = nombre_qsa[0].upper() + nombre_qsa[1:] # mod = getattr(qsa_dict_modules, model_name, None) # if mod is None: # mod = base_model(nombre) # if mod: # setattr(qsa_dict_modules, model_name, mod) # NOTE: Use application.qsadictmodules db_name = application.PROJECT.conn_manager.mainConn().DBName() module = None file_path = filedir( config.value("ebcomportamiento/temp_dir"), "cache", db_name, "models", "%s_model.py" % nombre, ) if os.path.exists(file_path): module_path = "%s_model" % (nombre) # if module_path in sys.modules: # # print("Recargando", module_path) # try: # module = importlib.reload(sys.modules[module_path]) # except Exception as exc: # logger.warning("Error recargando módulo:\n%s\n%s", exc, traceback.format_exc()) # pass # else: # print("Cargando", module_path) try: spec = importlib.util.spec_from_file_location( module_path, file_path) # type: ignore module = importlib.util.module_from_spec(spec) # type: ignore sys.modules[spec.name] = module spec.loader.exec_module(module) except Exception as exc: LOGGER.warning("Error cargando módulo:\n%s\n%s", exc, traceback.format_exc()) pass # models_[nombre] = mod # if mod: # setattr(qsa_dict_modules, model_name, mod) # print(3, nombre, mod) return module
def setUpClass(cls) -> None: """Ensure pineboo is initialized for testing.""" from pineboolib.core.utils.utils_base import filedir db_name = "temp_db" dirs = [True, filedir("./application/staticloader/tests/fixtures")] config.set_value("StaticLoader/%s/enabled" % (db_name), True) # Para activar carga estática config.set_value("StaticLoader/%s/dirs" % db_name, dirs) init_testing()
def staticLoaderSetup(self) -> None: """ Display dialog box to configure static load from local disk. """ ui = cast( QtWidgets.QDialog, self.createUI( utils_base.filedir( "./system_module/forms/FLStaticLoaderUI.ui")), ) pnmodulesstaticloader.PNStaticLoader.setup(self.static_db_info_, ui)
def init_conn(self, connection: "pnconnection.PNConnection") -> None: """Initialize project with a connection.""" # if self._conn is not None: # del self._conn # self._conn = None self._conn_manager.setMainConn(connection) self.apppath = utils_base.filedir("..") self.delete_cache = settings.config.value( "ebcomportamiento/deleteCache", False) self.parse_project = settings.config.value( "ebcomportamiento/parseProject", False)
def test_basic(self) -> None: """Test basic functions.""" from pineboolib.application.parsers.mtdparser import pnmtdparser from pineboolib.core.utils.utils_base import filedir import os file = filedir( "%s/cache/temp_db/sys/file.mtd/flmodules_model.py" % config.value("ebcomportamiento/temp_dir") ) if os.path.exists(file): os.remove(file) pnmtdparser.mtd_parse("flmodules") self.assertTrue(os.path.exists(file))
def test_formRecord(self) -> None: """Test formRecord widget.""" mng_modules = application.PROJECT.conn_manager.managerModules() from pineboolib.core.utils.utils_base import filedir file_1 = filedir( "./application/parsers/qt3uiparser/tests/fixtures/form_record_qt3.ui" ) widget = mng_modules.createUI(file_1) self.assertTrue(widget) bt_01 = widget.findChild(QtWidgets.QWidget, "pb_uno", QtCore.Qt.FindChildrenRecursively) self.assertTrue(bt_01)
def test_mainForm(self) -> None: """Test mainForm widget.""" mng_modules = application.PROJECT.conn_manager.managerModules() from pineboolib.core.utils.utils_base import filedir file_1 = filedir( "./application/parsers/qt3uiparser/tests/fixtures/main_form_qt3.ui" ) widget = mng_modules.createUI(file_1) self.assertTrue(widget) action = widget.findChild(QtWidgets.QAction, "ebcomportamiento") self.assertTrue(action)
def loadAllIdModules(self) -> None: """ Load the list of all module identifiers. """ self.list_all_id_modules_ = [] self.list_all_id_modules_.append("sys") self.dict_info_mods_ = {} q = pnsqlquery.PNSqlQuery(None, "dbAux") q.setTablesList("flmodules,flareas") q.setSelect( "idmodulo,flmodules.idarea,flmodules.descripcion,version,icono,flareas.descripcion" ) q.setFrom( "flmodules left join flareas on flmodules.idarea = flareas.idarea") q.setWhere("1 = 1") q.setForwardOnly(True) q.exec_() # q.exec_("SELECT idmodulo,flmodules.idarea,flmodules.descripcion,version,icono,flareas.descripcion " # "FROM flmodules left join flareas on flmodules.idarea = flareas.idarea") sys_module_found_ = False while q.next(): info_module_ = FLInfoMod() info_module_.idModulo = str(q.value(0)) info_module_.idArea = str(q.value(1)) info_module_.descripcion = str(q.value(2)) info_module_.version = str(q.value(3)) info_module_.icono = str(q.value(4)) info_module_.areaDescripcion = str(q.value(5)) self.dict_info_mods_[info_module_.idModulo.upper()] = info_module_ if not info_module_.idModulo == "sys": self.list_all_id_modules_.append(info_module_.idModulo) else: sys_module_found_ = True if not sys_module_found_: info_module_ = FLInfoMod() info_module_.idModulo = "sys" info_module_.idArea = "sys" info_module_.descripcion = "Administracion" info_module_.version = "0.0" info_module_.icono = self.contentFS( "%s/%s" % (utils_base.filedir("./system_module"), "/sys.xpm")) info_module_.areaDescripcion = "Sistema" self.dict_info_mods_[info_module_.idModulo.upper()] = info_module_
def load_models() -> None: """Load all sqlAlchemy models.""" from pineboolib.application.qsadictmodules import QSADictModules if application.PROJECT.conn_manager is None: raise Exception("Project is not connected yet") main_conn = application.PROJECT.conn_manager.mainConn() db_name = main_conn.DBName() tables = main_conn.tables() QSADictModules.save_other("Base", main_conn.declarative_base()) QSADictModules.save_other("session", main_conn.session()) QSADictModules.save_other("engine", main_conn.engine()) for table in tables: try: mod = base_model(table) except Exception: mod = None if mod is not None: model_name = "%s%s" % (table[0].upper(), table[1:]) class_ = getattr(mod, model_name, None) if class_ is not None: QSADictModules.save_other(model_name, class_) for root, dirs, files in os.walk( filedir(config.value("ebcomportamiento/temp_dir"), "cache", db_name, "models")): for nombre in files: # Buscamos los presonalizados if nombre.endswith("pyc"): continue nombre = nombre.replace("_model.py", "") mod = load_model(nombre) if mod is not None: model_name = "%s%s" % (nombre[0].upper(), nombre[1:]) class_ = getattr(mod, model_name, None) if class_ is not None: QSADictModules.save_other(model_name, class_)
def load(self) -> None: """Load widget and show.""" from pineboolib import application from pineboolib.core.utils.utils_base import filedir dlg_ = filedir( "plugins/dgi/dgi_qt/dgi_objects/dlg_about/about_pineboo.ui") version_ = application.PROJECT.version self.ui: Any = application.PROJECT.conn_manager.managerModules( ).createUI(dlg_, None, self) if self.ui is None: raise Exception("Error creating UI About Dialog") self.ui.lbl_version.setText("Pineboo %s" % str(version_)) self.ui.btn_close.clicked.connect(self.ui.close) self.ui.btn_clipboard.clicked.connect(self.to_clipboard) self.ui.show() self.ui.lbl_librerias.setText(self.load_components())
def __init__(self): """Inicialize.""" splash_path = filedir( "./core/images/splashscreen/%s240.png" % ("dbadmin" if config.value("application/dbadmin_enabled") else "quick")) splash_pix = QtGui.QPixmap(splash_path) self._splash = QtWidgets.QSplashScreen(splash_pix, QtCore.Qt.WindowStaysOnTopHint) self._splash.setMask(splash_pix.mask()) frameGm = self._splash.frameGeometry() screen = QtWidgets.QApplication.desktop().screenNumber( QtWidgets.QApplication.desktop().cursor().pos()) centerPoint = QtWidgets.QApplication.desktop().screenGeometry( screen).center() frameGm.moveCenter(centerPoint) self._splash.move(frameGm.topLeft())
def load(self) -> None: """ Load the dlgconnect form. """ from pineboolib.fllegacy.flmanagermodules import FLManagerModules dlg_ = filedir("loader/dlgconnect/dlgconnect.ui") self._user_interface: Any = FLManagerModules.createUI(dlg_, None, self) if not self._user_interface: raise Exception("Error creating dlgConnect") # Centrado en pantalla frame_geo = self.frameGeometry() screen = QtWidgets.QApplication.desktop().screenNumber( QtWidgets.QApplication.desktop().cursor().pos()) center_point = QtWidgets.QApplication.desktop().screenGeometry( screen).center() frame_geo.moveCenter(center_point) self.move(frame_geo.topLeft()) self._user_interface.pbLogin.clicked.connect(self.open) self._user_interface.tbOptions.clicked.connect(self.toggleOptions) self._user_interface.pbSaveConnection.clicked.connect(self.saveProfile) self._user_interface.tbDeleteProfile.clicked.connect( self.deleteProfile) self._user_interface.tbEditProfile.clicked.connect(self.editProfile) self.cleanProfileForm() self._user_interface.cbDBType.currentIndexChanged.connect( self.updatePort) self._user_interface.cbProfiles.currentIndexChanged.connect( self.enablePassword) self._user_interface.cbAutoLogin.stateChanged.connect( self.cbAutoLogin_checked) self._user_interface.le_profiles.setText(self.profile_dir) self._user_interface.tb_profiles.clicked.connect( self.change_profile_dir) self.showOptions(False) self.loadProfiles() self._user_interface.leDescription.textChanged.connect( self.updateDBName) self._user_interface.installEventFilter(self)
def loadControls(self) -> None: """Load form controls.""" self.bottomToolbar = QtWidgets.QFrame() if self.bottomToolbar: self.bottomToolbar.setMaximumHeight(64) self.bottomToolbar.setMinimumHeight(16) hblay = QtWidgets.QHBoxLayout() hblay.setContentsMargins(0, 0, 0, 0) hblay.setSpacing(0) hblay.addStretch() self.bottomToolbar.setLayout(hblay) self.bottomToolbar.setFocusPolicy(QtCore.Qt.NoFocus) self.layout_.addWidget(self.bottomToolbar) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy(0), QtWidgets.QSizePolicy.Policy(0)) sizePolicy.setHeightForWidth(True) pbSize = self.iconSize if settings.config.value("application/isDebuggerMode", False): pushButtonExport = QtWidgets.QToolButton(self) pushButtonExport.setObjectName("pushButtonExport") pushButtonExport.setSizePolicy(sizePolicy) pushButtonExport.setMinimumSize(pbSize) pushButtonExport.setMaximumSize(pbSize) pushButtonExport.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-properties.png"))) pushButtonExport.setShortcut(Qt.QKeySequence(self.tr("F3"))) pushButtonExport.setWhatsThis("Exportar a XML(F3)") pushButtonExport.setToolTip("Exportar a XML(F3)") pushButtonExport.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(pushButtonExport) pushButtonExport.clicked.connect(self.exportToXml) if settings.config.value("ebcomportamiento/show_snaptshop_button", False): push_button_snapshot = QtWidgets.QToolButton(self) push_button_snapshot.setObjectName("pushButtonSnapshot") push_button_snapshot.setSizePolicy(sizePolicy) push_button_snapshot.setMinimumSize(pbSize) push_button_snapshot.setMaximumSize(pbSize) push_button_snapshot.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-paste.png"))) push_button_snapshot.setShortcut(Qt.QKeySequence( self.tr("F8"))) push_button_snapshot.setWhatsThis("Capturar pantalla(F8)") push_button_snapshot.setToolTip("Capturar pantalla(F8)") push_button_snapshot.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(push_button_snapshot) push_button_snapshot.clicked.connect(self.saveSnapShot) spacer = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) self.bottomToolbar.layout().addItem(spacer) if not self.pushButtonAccept: self.pushButtonAccept = QtWidgets.QToolButton(self) self.pushButtonAccept.setObjectName("pushButtonAccept") self.pushButtonAccept.clicked.connect(self.accept) self.pushButtonAccept.setSizePolicy(sizePolicy) self.pushButtonAccept.setMaximumSize(pbSize) self.pushButtonAccept.setMinimumSize(pbSize) self.pushButtonAccept.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-save.png"))) # pushButtonAccept->setAccel(Qt.QKeySequence(Qt::Key_F10)); FIXME self.pushButtonAccept.setFocus() self.pushButtonAccept.setWhatsThis( "Seleccionar registro actual y cerrar formulario (F10)") self.pushButtonAccept.setToolTip( "Seleccionar registro actual y cerrar formulario (F10)") self.pushButtonAccept.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(self.pushButtonAccept) self.pushButtonAccept.show() if not self.pushButtonCancel: self.pushButtonCancel = QtWidgets.QToolButton(self) self.pushButtonCancel.setObjectName("pushButtonCancel") self.pushButtonCancel.clicked.connect(self.reject) self.pushButtonCancel.setSizePolicy(sizePolicy) self.pushButtonCancel.setMaximumSize(pbSize) self.pushButtonCancel.setMinimumSize(pbSize) self.pushButtonCancel.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-stop.png"))) self.pushButtonCancel.setFocusPolicy(QtCore.Qt.NoFocus) # pushButtonCancel->setAccel(Esc); FIXME self.pushButtonCancel.setWhatsThis( "Cerrar formulario sin seleccionar registro (Esc)") self.pushButtonCancel.setToolTip( "Cerrar formulario sin seleccionar registro (Esc)") self.bottomToolbar.layout().addWidget(self.pushButtonCancel) self.pushButtonCancel.show() if self.cursor_ is None: raise Exception("Cursor is empty!.") self.cursor_.setEdition(False) self.cursor_.setBrowse(False) self.cursor_.recordChoosed.connect(self.accept)
def loadControls(self) -> None: """Load widgets for this form.""" if self.pushButtonAcceptContinue: self.pushButtonAcceptContinue.hide() if self.pushButtonAccept: self.pushButtonAccept.hide() if self.pushButtonCancel: self.pushButtonCancel.hide() if self.pushButtonFirst: self.pushButtonFirst.hide() if self.pushButtonPrevious: self.pushButtonPrevious.hide() if self.pushButtonNext: self.pushButtonNext.hide() if self.pushButtonLast: self.pushButtonLast.hide() if self.bottomToolbar and self.toolButtonClose: self.toolButtonClose.hide() self.bottomToolbar = QtWidgets.QFrame() if self.bottomToolbar: self.bottomToolbar.setMinimumSize(self.iconSize) hblay = QtWidgets.QHBoxLayout() hblay.setContentsMargins(0, 0, 0, 0) hblay.setSpacing(0) hblay.addStretch() self.bottomToolbar.setLayout(hblay) self.bottomToolbar.setFocusPolicy(QtCore.Qt.NoFocus) self.layout_.addWidget(self.bottomToolbar) else: raise Exception("bottomToolbar is missing!") # if self.layout: # self.layout = None # Limpiamos la toolbar sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy(0), QtWidgets.QSizePolicy.Policy(0)) sizePolicy.setHeightForWidth(True) pbSize = self.iconSize if settings.config.value("application/isDebuggerMode", False): pushButtonExport = QtWidgets.QToolButton() pushButtonExport.setObjectName("pushButtonExport") pushButtonExport.setSizePolicy(sizePolicy) pushButtonExport.setMinimumSize(pbSize) pushButtonExport.setMaximumSize(pbSize) pushButtonExport.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-properties.png"))) pushButtonExport.setShortcut(Qt.QKeySequence(self.tr("F3"))) pushButtonExport.setWhatsThis("Exportar a XML(F3)") pushButtonExport.setToolTip("Exportar a XML(F3)") pushButtonExport.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(pushButtonExport) pushButtonExport.clicked.connect(self.exportToXml) if settings.config.value("ebcomportamiento/show_snaptshop_button", False): push_button_snapshot = QtWidgets.QToolButton() push_button_snapshot.setObjectName("pushButtonSnapshot") push_button_snapshot.setSizePolicy(sizePolicy) push_button_snapshot.setMinimumSize(pbSize) push_button_snapshot.setMaximumSize(pbSize) push_button_snapshot.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-paste.png"))) push_button_snapshot.setShortcut(Qt.QKeySequence( self.tr("F8"))) push_button_snapshot.setWhatsThis("Capturar pantalla(F8)") push_button_snapshot.setToolTip("Capturar pantalla(F8)") push_button_snapshot.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(push_button_snapshot) push_button_snapshot.clicked.connect(self.saveSnapShot) spacer = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) self.bottomToolbar.layout().addItem(spacer) if self.cursor().modeAccess() in (self.cursor().Edit, self.cursor().Browse): if not self.pushButtonFirst: self.pushButtonFirst = QtWidgets.QToolButton() self.pushButtonFirst.setObjectName("pushButtonFirst") self.pushButtonFirst.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-goto-first-ltr.png"))) self.pushButtonFirst.clicked.connect(self.firstRecord) self.pushButtonFirst.setSizePolicy(sizePolicy) self.pushButtonFirst.setMaximumSize(pbSize) self.pushButtonFirst.setMinimumSize(pbSize) self.pushButtonFirst.setShortcut(Qt.QKeySequence( self.tr("F5"))) self.pushButtonFirst.setWhatsThis( "Aceptar los cambios e ir al primer registro (F5)") self.pushButtonFirst.setToolTip( "Aceptar los cambios e ir al primer registro (F5)") self.pushButtonFirst.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(self.pushButtonFirst) # self.pushButtonFirst.show() if not self.pushButtonPrevious: self.pushButtonPrevious = QtWidgets.QToolButton() self.pushButtonPrevious.setObjectName("pushButtonPrevious") self.pushButtonPrevious.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-go-back-ltr.png"))) self.pushButtonPrevious.clicked.connect(self.previousRecord) self.pushButtonPrevious.setSizePolicy(sizePolicy) self.pushButtonPrevious.setMaximumSize(pbSize) self.pushButtonPrevious.setMinimumSize(pbSize) self.pushButtonPrevious.setShortcut( Qt.QKeySequence(self.tr("F6"))) self.pushButtonPrevious.setWhatsThis( "Aceptar los cambios e ir al registro anterior (F6)") self.pushButtonPrevious.setToolTip( "Aceptar los cambios e ir al registro anterior (F6)") self.pushButtonPrevious.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(self.pushButtonPrevious) # self.pushButtonPrevious.show() if not self.pushButtonNext: self.pushButtonNext = QtWidgets.QToolButton() self.pushButtonNext.setObjectName("pushButtonNext") self.pushButtonNext.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-go-back-rtl.png"))) self.pushButtonNext.clicked.connect(self.nextRecord) self.pushButtonNext.setSizePolicy(sizePolicy) self.pushButtonNext.setMaximumSize(pbSize) self.pushButtonNext.setMinimumSize(pbSize) self.pushButtonNext.setShortcut(Qt.QKeySequence(self.tr("F7"))) self.pushButtonNext.setWhatsThis( "Aceptar los cambios e ir al registro siguiente (F7)") self.pushButtonNext.setToolTip( "Aceptar los cambios e ir al registro siguiente (F7)") self.pushButtonNext.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(self.pushButtonNext) # self.pushButtonNext.show() if not self.pushButtonLast: self.pushButtonLast = QtWidgets.QToolButton() self.pushButtonLast.setObjectName("pushButtonLast") self.pushButtonLast.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-goto-last-ltr.png"))) self.pushButtonLast.clicked.connect(self.lastRecord) self.pushButtonLast.setSizePolicy(sizePolicy) self.pushButtonLast.setMaximumSize(pbSize) self.pushButtonLast.setMinimumSize(pbSize) self.pushButtonLast.setShortcut(Qt.QKeySequence(self.tr("F8"))) self.pushButtonLast.setWhatsThis( "Aceptar los cambios e ir al último registro (F8)") self.pushButtonLast.setToolTip( "Aceptar los cambios e ir al último registro (F8)") self.pushButtonLast.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(self.pushButtonLast) # self.pushButtonLast.show() if not self.cursor().modeAccess() == self.cursor().Browse: self.pushButtonAcceptContinue = QtWidgets.QToolButton() self.pushButtonAcceptContinue.setObjectName( "pushButtonAcceptContinue") self.pushButtonAcceptContinue.clicked.connect(self.acceptContinue) self.pushButtonAcceptContinue.setSizePolicy(sizePolicy) self.pushButtonAcceptContinue.setMaximumSize(pbSize) self.pushButtonAcceptContinue.setMinimumSize(pbSize) self.pushButtonAcceptContinue.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-refresh.png"))) self.pushButtonAcceptContinue.setShortcut( Qt.QKeySequence(self.tr("F9"))) self.pushButtonAcceptContinue.setWhatsThis( "Aceptar los cambios y continuar con la edición de un nuevo registro (F9)" ) self.pushButtonAcceptContinue.setToolTip( "Aceptar los cambios y continuar con la edición de un nuevo registro (F9)" ) self.pushButtonAcceptContinue.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget( self.pushButtonAcceptContinue) if not self.showAcceptContinue_: self.pushButtonAcceptContinue.close() # self.pushButtonAcceptContinue.show() if not self.pushButtonAccept: self.pushButtonAccept = QtWidgets.QToolButton() self.pushButtonAccept.setObjectName("pushButtonAccept") self.pushButtonAccept.clicked.connect(self.accept) self.pushButtonAccept.setSizePolicy(sizePolicy) self.pushButtonAccept.setMaximumSize(pbSize) self.pushButtonAccept.setMinimumSize(pbSize) self.pushButtonAccept.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-save.png"))) self.pushButtonAccept.setShortcut(Qt.QKeySequence(self.tr("F10"))) self.pushButtonAccept.setWhatsThis( "Aceptar los cambios y cerrar formulario (F10)") self.pushButtonAccept.setToolTip( "Aceptar los cambios y cerrar formulario (F10)") self.pushButtonAccept.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(self.pushButtonAccept) # self.pushButtonAccept.show() if not self.pushButtonCancel: self.pushButtonCancel = QtWidgets.QToolButton() self.pushButtonCancel.setObjectName("pushButtonCancel") try: self.cursor().autoCommit.connect(self.disablePushButtonCancel) except Exception: pass self.pushButtonCancel.clicked.connect(self.reject) self.pushButtonCancel.setSizePolicy(sizePolicy) self.pushButtonCancel.setMaximumSize(pbSize) self.pushButtonCancel.setMinimumSize(pbSize) self.pushButtonCancel.setShortcut(Qt.QKeySequence(self.tr("Esc"))) self.pushButtonCancel.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-stop.png"))) if not self.cursor().modeAccess() == self.cursor().Browse: self.pushButtonCancel.setFocusPolicy(QtCore.Qt.NoFocus) self.pushButtonCancel.setWhatsThis( "Cancelar los cambios y cerrar formulario (Esc)") self.pushButtonCancel.setToolTip( "Cancelar los cambios y cerrar formulario (Esc)") else: self.pushButtonCancel.setFocusPolicy(QtCore.Qt.StrongFocus) self.pushButtonCancel.setFocus() # pushButtonCancel->setAccel(4096); FIXME self.pushButtonCancel.setWhatsThis( "Aceptar y cerrar formulario (Esc)") self.pushButtonCancel.setToolTip( "Aceptar y cerrar formulario (Esc)") # pushButtonCancel->setDefault(true); self.bottomToolbar.layout().addItem( QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) self.bottomToolbar.layout().addWidget(self.pushButtonCancel) # self.pushButtonAccept.show() self.setFocusPolicy(QtCore.Qt.NoFocus) # self.toolButtonAccept = QtGui.QToolButton() # self.toolButtonAccept.setIcon(QtGui.QIcon(utils_base.filedir("./core/images/icons","gtk-add.png"))) # self.toolButtonAccept.clicked.connect(self.validateForm) # self.bottomToolbar.layout.addWidget(self.toolButtonAccept) self.inicializeControls()
class ProjectConfig: """ Read and write XML on profiles. Represents a database connection configuration. """ SAVE_VERSION = VERSION_1_2 #: Version for saving #: Folder where to read/write project configs. profile_dir: str = filedir( config.value("ebcomportamiento/profiles_folder", "%s/Pineboo/profiles" % Path.home())) version: VersionNumber #: Version number for the profile read. fernet: Optional[Fernet] #: Cipher used, if any. database: str #: Database Name, file path to store it, or :memory: host: Optional[str] #: DB server Hostname. None for local files. port: Optional[int] #: DB server port. None for local files. username: Optional[str] #: Database User login name. password: Optional[str] #: Database User login password. type: str #: Driver Type name to use when connecting project_password: str #: Password to cipher when load/saving. Empty string for no ciphering. password_required: bool #: True if a password is required to read data. (Was partially loaded.) description: str #: Project name in GUI filename: str #: File path to read / write this project from / to def __init__( self, database: Optional[str] = None, host: Optional[str] = None, port: Optional[int] = None, type: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, load_xml: Optional[str] = None, connstring: Optional[str] = None, description: Optional[str] = None, filename: Optional[str] = None, project_password: str = "", ) -> None: """Initialize.""" self.project_password = project_password self.password_required = False self.version = self.SAVE_VERSION self.fernet = None if connstring: username, password, type, host, port, database = self.translate_connstring( connstring) elif load_xml: self.filename = os.path.join( self.profile_dir, load_xml if load_xml.endswith("xml") else "%s.xml" % load_xml) self.load_projectxml() return if database is None: raise ValueError( "Database is mandatory. Or use load_xml / connstring params") if type is None: raise ValueError( "Type is mandatory. Or use load_xml / connstring params") self.database = database self.host = host self.port = port self.type = type self.username = username self.password = password self.description = description if description else "unnamed" if filename is None: file_basename = self.description.lower().replace(" ", "_") self.filename = os.path.join(self.profile_dir, "%s.xml" % file_basename) else: self.filename = os.path.join(self.profile_dir, filename) def get_uri(self, show_password: bool = False) -> str: """Get connection as an URI.""" host_port = "" if self.host: host_port += self.host if self.port: host_port += ":%d" % self.port user_pass = "" if self.username: user_pass += self.username if self.password: if show_password: user_pass += ":%s" % self.password else: pass_bytes: bytes = hashlib.sha256( self.password.encode()).digest() user_pass += ":*" + base64.b64encode(pass_bytes).decode()[:4] if user_pass: user_pass = "******" % user_pass uri = host_port + user_pass if self.database: if uri: uri += "/" uri += self.database return "[%s]://%s" % (self.type, uri) def __repr__(self) -> str: """Display the information in text mode.""" if self.project_password: # 4 chars in base-64 is 3 bytes. 256**3 should be enough to know if you have the wrong # password. pass_bytes: bytes = hashlib.sha256( self.project_password.encode()).digest() passwd = "-" + base64.b64encode(pass_bytes).decode()[:4] else: passwd = "" return "<ProjectConfig%s name=%r uri=%r>" % ( passwd, self.description, self.get_uri(show_password=False), ) def __eq__(self, other: Any) -> bool: """Test for equality.""" if not isinstance(other, ProjectConfig): return False if other.type != self.type: return False if other.get_uri(show_password=True) != self.get_uri( show_password=True): return False if other.description != self.description: return False if other.project_password != self.project_password: return False return True def load_projectxml(self) -> bool: """Collect the connection information from an xml file.""" file_name = self.filename if not os.path.isfile(file_name): raise ValueError("El proyecto %r no existe." % file_name) tree = ET.parse(file_name) root = tree.getroot() version = VersionNumber(root.get("Version"), default="1.0") self.version = version self.description = "" for xmldescription in root.findall("name"): self.description = xmldescription.text or "" profile_pwd = "" for profile in root.findall("profile-data"): profile_pwd = getattr(profile.find("password"), "text", "") if profile_pwd: break self.password_required = True self.checkProfilePasswordForVersion(self.project_password, profile_pwd, version) if self.project_password and self.version > VERSION_1_1: key_salt = hashlib.sha256(profile_pwd.encode()).digest() key = hashlib.pbkdf2_hmac("sha256", self.project_password.encode(), key_salt, 10000) key64 = base64.urlsafe_b64encode(key) self.fernet = Fernet(key64) else: self.fernet = None from pineboolib.application.database.pnsqldrivers import PNSqlDrivers sql_drivers_manager = PNSqlDrivers() self.database = self.retrieveCipherSubElement(root, "database-name") for db in root.findall("database-server"): self.host = self.retrieveCipherSubElement(db, "host") port_text = self.retrieveCipherSubElement(db, "port") self.port = int(port_text) if port_text else None self.type = self.retrieveCipherSubElement(db, "type") # FIXME: Move this to project, or to the connection handler. if self.type not in sql_drivers_manager.aliasList(): LOGGER.warning( "Esta versión de pineboo no soporta el driver '%s'" % self.type) for credentials in root.findall("database-credentials"): self.username = self.retrieveCipherSubElement( credentials, "username") self.password = self.retrieveCipherSubElement( credentials, "password") if self.password and self.fernet is None: self.password = base64.b64decode(self.password).decode() self.password_required = False return True @classmethod def encodeProfilePasswordForVersion(cls, password: str, save_version: VersionNumber) -> str: """ Hash a password for a profile/projectconfig using the protocol for specified version. """ if password == "": return "" if save_version < VERSION_1_1: return password if save_version < VERSION_1_2: return hashlib.sha256(password.encode()).hexdigest() # Minimum salt size recommended is 8 bytes # multiple of 3 bytes as it is shorter in base64 salt = os.urandom(9) hmac = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 10000) dict_passwd = { # Algorithm: # .. pbkdf2: short algorithm name # .. sha256: hash function used # .. 4: number of zeroes used on iterations "algorithm": "pbkdf2-sha256-4", "salt": base64.b64encode(salt).decode(), "hash": base64.b64encode(hmac).decode(), } hashed_password = "******" % dict_passwd return hashed_password @classmethod def checkProfilePasswordForVersion(cls, user_pwd: str, profile_pwd: str, version: VersionNumber) -> None: """ Check a saved password against a user-supplied one. user_pwd: User-supplied password in clear text. profile_pwd: Raw data saved as password in projectconfig file. version: Version number used for checks. This function returns None and raises PasswordMismatchError if the password is wrong. We can only check if it is good. It's not a good idea to check if two encoded passwords are the same, because most secure methods will store different encoded versions every time we try to encode again. """ if not profile_pwd: return if version < VERSION_1_1: if user_pwd == profile_pwd: return raise PasswordMismatchError("La contraseña es errónea") if version < VERSION_1_2: user_hash = hashlib.sha256(user_pwd.encode()).hexdigest() if profile_pwd == user_hash: return raise PasswordMismatchError("La contraseña es errónea") algo, *algo_extra = profile_pwd.split(":") if algo != "pbkdf2-sha256-4": raise Exception("Unsupported password algorithm %r" % algo) salt64, hash64 = algo_extra salt = base64.b64decode(salt64.encode()) user_hash2 = hashlib.pbkdf2_hmac("sha256", user_pwd.encode(), salt, 10000) user_hash64 = base64.b64encode(user_hash2).decode() if user_hash64 == hash64: return raise PasswordMismatchError("La contraseña es errónea") def createCipherSubElement(self, parent: ET.Element, tagname: str, text: str) -> ET.Element: """Create a XML SubElement ciphered if self.fernet is present.""" child = ET.SubElement(parent, tagname) if self.fernet is None: child.text = text return child # NOTE: This method returns ciphertext even for empty strings! This is intended. # ... this is to avoid anyone knowing if a field is empty or not. if len(text) < 64: # Right Pad with new line at least up to 64 bytes. Avoid giving out field size. text = text.ljust(64, "\n") encoded_bytes = self.fernet.encrypt(text.encode()) encoded_text = base64.b64encode(encoded_bytes).decode() child.set("cipher-method", "cryptography.Fernet") child.set("cipher-text", encoded_text) return child def retrieveCipherSubElement(self, parent: ET.Element, tagname: str) -> str: """Get a XML SubElement ciphered if self.fernet is present.""" child = parent.find(tagname) if child is None: raise ValueError("Tag %r not present" % tagname) cipher_method = child.get("cipher-method") if cipher_method is None: return child.text or "" if self.fernet is None: raise Exception( "Tried to load ciphered tag %r with no loaded cipher" % tagname) cipher_text = child.get("cipher-text") if cipher_method != "cryptography.Fernet": raise ValueError("Cipher method %r not supported." % cipher_method) if not cipher_text: raise ValueError("Missing ciphertext for %r" % tagname) cipher_bytes = base64.b64decode(cipher_text.encode()) text = self.fernet.decrypt(cipher_bytes).decode() text = text.rstrip("\n") return text def save_projectxml(self, overwrite_existing: bool = True) -> None: """ Save the connection. """ profile = ET.Element("Profile") profile.set("Version", str(self.SAVE_VERSION)) description = self.description filename = self.filename if not os.path.exists(self.profile_dir): os.mkdir(self.profile_dir) if not overwrite_existing and os.path.exists(filename): raise ProfileAlreadyExistsError passwDB = self.password or "" profile_user = ET.SubElement(profile, "profile-data") profile_password = ET.SubElement(profile_user, "password") profile_password.text = self.encodeProfilePasswordForVersion( self.project_password, self.SAVE_VERSION) if self.project_password and self.SAVE_VERSION > VERSION_1_1: key_salt = hashlib.sha256(profile_password.text.encode()).digest() key = hashlib.pbkdf2_hmac("sha256", self.project_password.encode(), key_salt, 10000) key64 = base64.urlsafe_b64encode(key) self.fernet = Fernet(key64) else: # Mask the password if no cipher is used! passwDB = base64.b64encode(passwDB.encode()).decode() self.fernet = None name = ET.SubElement(profile, "name") name.text = description dbs = ET.SubElement(profile, "database-server") self.createCipherSubElement(dbs, "type", text=self.type) self.createCipherSubElement(dbs, "host", text=self.host or "") self.createCipherSubElement(dbs, "port", text=str(self.port) if self.port else "") dbc = ET.SubElement(profile, "database-credentials") self.createCipherSubElement(dbc, "username", text=self.username or "") self.createCipherSubElement(dbc, "password", text=passwDB) self.createCipherSubElement(profile, "database-name", text=self.database) pretty_print_xml(profile) tree = ET.ElementTree(profile) tree.write(filename, xml_declaration=True, encoding="utf-8") self.version = self.SAVE_VERSION @classmethod def translate_connstring( cls, connstring: str) -> Tuple[str, str, str, str, int, str]: """ Translate a DSN connection string into user, pass, etc. Accept a "connstring" parameter that has the form user @ host / dbname and returns all parameters separately. It takes into account the default values and the different abbreviations that exist. """ user = "******" passwd = "" host = "127.0.0.1" port = "5432" driver_alias = "PostgreSQL (PSYCOPG2)" user_pass = None host_port = None if "/" not in connstring: dbname = connstring if not re.match(r"\w+", dbname): raise ValueError("base de datos no valida") return user, passwd, driver_alias, host, int(port), dbname uphpstring = connstring[:connstring.rindex("/")] dbname = connstring[connstring.rindex("/") + 1:] up, hp = uphpstring.split("@") conn_list = [None, None, up, hp] _user_pass, _host_port = conn_list[-2], conn_list[-1] if _user_pass: user_pass = _user_pass.split(":") + ["", "", ""] user, passwd, driver_alias = ( user_pass[0], user_pass[1] or passwd, user_pass[2] or driver_alias, ) if user_pass[3]: raise ValueError( "La cadena de usuario debe tener el formato user:pass:driver." ) if _host_port: host_port = _host_port.split(":") + [""] host, port = host_port[0], host_port[1] or port if host_port[2]: raise ValueError("La cadena de host debe ser host:port.") if not re.match(r"\w+", user): raise ValueError("Usuario no valido") if not re.match(r"\w+", dbname): raise ValueError("base de datos no valida") if not re.match(r"\d+", port): raise ValueError("puerto no valido") LOGGER.debug( "user:%s, passwd:%s, driver_alias:%s, host:%s, port:%s, dbname:%s", user, "*" * len(passwd), driver_alias, host, port, dbname, ) return user, passwd, driver_alias, host, int(port), dbname
def run(self) -> bool: """Run project. Connects to DB and loads data.""" self.pending_conversion_list = [] if self.actions: del self.actions if self.tables: del self.tables self.actions = {} self.tables = {} if self.dgi is None: raise Exception("DGI not loaded") if not self.conn_manager or "main_conn" not in self.conn_manager.connections_dict.keys( ): raise exceptions.NotConnectedError( "Cannot execute Pineboo Project without a connection in place") conn = self.conn_manager.mainConn() db_name = conn.DBName() # TODO: Refactorizar esta función en otras más sencillas # Preparar temporal if self.delete_cache and os.path.exists(path._dir( "cache/%s" % db_name)): self.message_manager().send("splash", "showMessage", ["Borrando caché ..."]) LOGGER.debug("DEVELOP: delete_cache Activado\nBorrando %s", path._dir("cache/%s" % db_name)) for root, dirs, files in os.walk(path._dir("cache/%s" % db_name), topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) else: keep_images = settings.config.value( "ebcomportamiento/keep_general_cache", False) if keep_images is False: for file_name in os.listdir(self.tmpdir): if file_name.find(".") > -1 and not file_name.endswith( "sqlite3"): file_path = os.path.join(self.tmpdir, file_name) try: os.remove(file_path) except Exception: LOGGER.warning( "No se ha podido borrar %s al limpiar la cache", file_path) pass if not os.path.exists(path._dir("cache")): os.makedirs(path._dir("cache")) if not os.path.exists(path._dir("cache/%s" % db_name)): os.makedirs(path._dir("cache/%s" % db_name)) # Conectar: # Se verifica que existen estas tablas for table in ( "flareas", "flmodules", "flfiles", "flgroups", "fllarge", "flserial", "flusers", "flvar", "flmetadata", "flsettings", "flupdates", "flmetadata", "flseqs", "flsettings", ): if not self.conn_manager.manager().existsTable(table): self.conn_manager.manager().createSystemTable(table) cursor_ = self.conn_manager.dbAux().cursor() self.areas = {} cursor_.execute( """ SELECT idarea, descripcion FROM flareas WHERE 1 = 1""") for idarea, descripcion in list(cursor_): self.areas[idarea] = AreaStruct(idarea=idarea, descripcion=descripcion) self.areas["sys"] = AreaStruct(idarea="sys", descripcion="Area de Sistema") # Obtener módulos activos cursor_.execute( """ SELECT idarea, idmodulo, descripcion, icono FROM flmodules WHERE bloqueo = %s """ % conn.driver().formatValue("bool", "True", False)) self.modules = {} for idarea, idmodulo, descripcion, icono in cursor_: icono = xpm.cache_xpm(icono) self.modules[idmodulo] = module.Module(idarea, idmodulo, descripcion, icono) file_object = open( utils_base.filedir(utils_base.get_base_dir(), "system_module", "sys.xpm"), "r") icono = file_object.read() file_object.close() # icono = clearXPM(icono) self.modules["sys"] = module.Module("sys", "sys", "Administración", icono) cursor_.execute( """ SELECT idmodulo, nombre, sha FROM flfiles WHERE NOT sha = '' ORDER BY idmodulo, nombre """ ) file_1 = open(path._dir("project.txt"), "w") self.files = {} count = 0 list_files: List[str] = [] for idmodulo, nombre, sha in list(cursor_): if not self.dgi.accept_file(nombre): continue count += 1 if idmodulo not in self.modules: continue # I fileobj = file.File(idmodulo, nombre, sha, db_name=db_name) if nombre in self.files: LOGGER.warning("run: file %s already loaded, overwritting..." % nombre) self.files[nombre] = fileobj self.modules[idmodulo].add_project_file(fileobj) file_1.write(fileobj.filekey + "\n") fileobjdir = os.path.dirname(path._dir("cache", fileobj.filekey)) file_name = path._dir("cache", fileobj.filekey) if not os.path.exists(fileobjdir): os.makedirs(fileobjdir) if os.path.exists(file_name): if file_name.endswith(".qs"): folder_path = os.path.dirname(file_name) static_flag = "%s/STATIC" % folder_path file_name_py = "%s.py" % file_name[:-3] if os.path.exists(static_flag): os.remove(static_flag) if os.path.exists(file_name): os.remove(file_name) if os.path.exists(file_name_py): os.remove(file_name_py) elif os.path.exists(file_name_py): continue elif file_name.endswith(".mtd"): if settings.config.value( "ebcomportamiento/orm_enabled", False) and not settings.config.value( "ebcomportamiento/orm_parser_disabled", False): if os.path.exists( "%s_model.py" % path._dir("cache", fileobj.filekey[:-4])): continue else: continue cur2 = self.conn_manager.useConn("dbAux").cursor() sql = ( "SELECT contenido FROM flfiles WHERE idmodulo = %s AND nombre = %s AND sha = %s" % ( conn.driver().formatValue("string", idmodulo, False), conn.driver().formatValue("string", nombre, False), conn.driver().formatValue("string", sha, False), )) cur2.execute(sql) for (contenido, ) in list(cur2): encode_ = "utf-8" if str(nombre).endswith( (".kut", ".ts", ".py")) else "ISO-8859-15" folder = path._dir( "cache", "/".join( fileobj.filekey.split("/") [:len(fileobj.filekey.split("/")) - 1]), ) if os.path.exists(folder) and not os.path.exists( file_name ): # Borra la carpeta si no existe el fichero destino for root, dirs, files in os.walk(folder): for file_item in files: os.remove(os.path.join(root, file_item)) if contenido and not os.path.exists(file_name): self.message_manager().send( "splash", "showMessage", ["Volcando a caché %s..." % nombre]) file_2 = open(file_name, "wb") txt = contenido.encode(encode_, "replace") file_2.write(txt) file_2.close() if self.parse_project and nombre.endswith(".qs"): if os.path.exists(file_name): list_files.append(file_name) file_1.close() self.message_manager().send("splash", "showMessage", ["Convirtiendo a Python ..."]) if list_files: self.parse_script_list(list_files) # Cargar el núcleo común del proyecto for root, dirs, files in os.walk( utils_base.filedir(utils_base.get_base_dir(), "system_module")): # list_files = [] for nombre in files: if root.find("modulos") == -1: fileobj = file.File("sys", nombre, basedir=root, db_name=db_name) self.files[nombre] = fileobj self.modules["sys"].add_project_file(fileobj) # if self.parse_project and nombre.endswith(".qs"): # self.parseScript(path._dir(root, nombre)) # list_files.append(path._dir(root, nombre)) # self.parse_script_lists(list_files) if settings.config.value( "ebcomportamiento/orm_enabled", False) and not settings.config.value( "ebcomportamiento/orm_load_disabled", False): self.message_manager().send("splash", "showMessage", ["Cargando objetos ..."]) from pineboolib.application.parsers.mtdparser import pnormmodelsfactory pnormmodelsfactory.load_models() # FIXME: ACLs needed at this level? # self.acl_ = FLAccessControlLists() # self.acl_.init() return True
def data(self, index: QtCore.QModelIndex, role: int = QtCore.Qt.DisplayRole) -> Any: """ Retrieve information about a record. (overload of QAbstractTableModel) Could return alignment, backgroun color, value... depending on role. @param index. Register position @param role. information type required @return solicited data """ row = index.row() col = index.column() field = self.metadata().indexFieldObject(col) _type = field.type() res_color_function: List[str] = [] if _type != "check": # r = [x for x in self._data[row]] # self._data[row] = r # d = r[col] # self.seekRow(row) # d = self._current_row_data[col] # d = self._data[row][col] result: Any = None if row not in self.grid_row_tmp.keys(): self.grid_row_tmp = {} self.grid_row_tmp[row] = self.driver_sql().getRow( row, self._curname, self.cursorDB() ) if not self.grid_row_tmp[row]: # refresh grid if cursor is deleted. self.refresh() return tuple = self.grid_row_tmp[row] if tuple: result = tuple[col] else: primary_key = str(self.value(row, self.metadata().primaryKey())) if primary_key not in self._check_column.keys(): result = QtWidgets.QCheckBox() self._check_column[primary_key] = result if self.parent_view and role in [QtCore.Qt.BackgroundRole, QtCore.Qt.ForegroundRole]: fun_get_color, iface = self.parent_view.functionGetColor() if fun_get_color is not None: context_ = None fun_name_ = None if fun_get_color.find(".") > -1: list_ = fun_get_color.split(".") from pineboolib.application.safeqsa import SafeQSA qsa_widget = SafeQSA.get_any(list_[0]) fun_name_ = list_[1] if qsa_widget: context_ = qsa_widget.iface else: context_ = iface fun_name_ = fun_get_color function_color = getattr(context_, fun_name_, None) if function_color is not None: field_name = field.name() field_value = result cursor = self._parent selected = False res_color_function = function_color( field_name, field_value, cursor, selected, _type ) else: raise Exception( "No se ha resuelto functionGetColor %s desde %s" % (fun_get_color, context_) ) # print("Data ", index, role) # print("Registros", self.rowCount()) # roles # 0 QtCore.Qt.DisplayRole # 1 QtCore.Qt.DecorationRole # 2 QtCore.Qt.EditRole # 3 QtCore.Qt.ToolTipRole # 4 QtCore.Qt.StatusTipRole # 5 QtCore.Qt.WhatThisRole # 6 QtCore.Qt.FontRole # 7 QtCore.Qt.TextAlignmentRole # 8 QtCore.Qt.BackgroundRole # 9 QtCore.Qt.ForegroundRole if role == QtCore.Qt.CheckStateRole and _type == "check": if primary_key in self._check_column.keys(): if self._check_column[primary_key].isChecked(): return QtCore.Qt.Checked return QtCore.Qt.Unchecked elif role == QtCore.Qt.TextAlignmentRole: result = QtCore.Qt.AlignVCenter if _type in ("int", "double", "uint"): result = result | QtCore.Qt.AlignRight elif _type in ("bool", "date", "time"): result = result | QtCore.Qt.AlignCenter elif _type in ("unlock", "pixmap"): result = result | QtCore.Qt.AlignHCenter return result elif role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole): if not field.visible(): result = None # r = self._vdata[row] elif _type == "bool": if result in (True, "1"): result = "Sí" else: result = "No" elif _type in ("unlock", "pixmap"): result = None elif _type in ("string", "stringlist", "timestamp") and not result: result = "" elif _type == "time" and result: result = str(result) elif _type == "date": # Si es str lo paso a datetime.date if isinstance(result, str): if len(result.split("-")[0]) == 4: result = date_conversion.date_amd_to_dma(result) if result: list_ = result.split("-") result = datetime.date(int(list_[2]), int(list_[1]), int(list_[0])) if isinstance(result, datetime.date): # Cogemos el locale para presentar lo mejor posible la fecha try: locale.setlocale(locale.LC_TIME, "") if os.name == "nt": date_format = "%%d/%%m/%%y" else: date_format = locale.nl_langinfo(locale.D_FMT) date_format = date_format.replace("y", "Y") # Año con 4 dígitos date_format = date_format.replace("/", "-") # Separadores result = result.strftime(date_format) except AttributeError: import platform self.logger.warning( "locale specific date format is not yet implemented for %s", platform.system(), ) elif _type == "check": return elif _type == "double": if result is not None: # d = QtCore.QLocale.system().toString(float(d), "f", field.partDecimal()) result = utils_base.format_double( result, field.partInteger(), field.partDecimal() ) elif _type in ("int", "uint"): if result is not None: result = QtCore.QLocale.system().toString(int(result)) if self.parent_view is not None: self.parent_view.resize_column(col, result) return result elif role == QtCore.Qt.DecorationRole: pixmap = None if _type in ("unlock", "pixmap") and self.parent_view: row_height = self.parent_view.rowHeight(row) # Altura row row_width = self.parent_view.columnWidth(col) if _type == "unlock": if result in (True, "1"): pixmap = QtGui.QPixmap( utils_base.filedir("./core/images/icons", "unlock.png") ) else: pixmap = QtGui.QPixmap( utils_base.filedir("./core/images/icons", "lock.png") ) else: if not self._parent.private_cursor._is_system_table: data = self.db().connManager().manager().fetchLargeValue(result) else: data = xpm.cache_xpm(result) pixmap = QtGui.QPixmap(data) if not pixmap.isNull(): new_size = row_height - 1 if new_size > row_width: new_size = row_width pixmap = pixmap.scaled(new_size, new_size) if self.parent_view.showAllPixmap() or row == self.parent_view.cur.at(): if pixmap and not pixmap.isNull() and self.parent_view: new_pixmap = QtGui.QPixmap(row_width, row_height) # w , h center_width = (row_width - pixmap.width()) / 2 center_height = (row_height - pixmap.height()) / 2 new_pixmap.fill(QtCore.Qt.transparent) painter = Qt.QPainter(new_pixmap) painter.drawPixmap( center_width, center_height, pixmap.width(), pixmap.height(), pixmap ) pixmap = new_pixmap return pixmap elif role == QtCore.Qt.BackgroundRole: if _type == "bool": if result in (True, "1"): result = QtGui.QBrush(QtCore.Qt.green) else: result = QtGui.QBrush(QtCore.Qt.red) elif _type == "check": obj_ = self._check_column[primary_key] result = ( QtGui.QBrush(QtCore.Qt.green) if obj_.isChecked() else QtGui.QBrush(QtCore.Qt.white) ) else: if res_color_function and len(res_color_function) and res_color_function[0] != "": color_ = QtGui.QColor(res_color_function[0]) style_ = getattr(QtCore.Qt, res_color_function[2], None) result = QtGui.QBrush(color_) result.setStyle(style_) else: result = None return result elif role == QtCore.Qt.ForegroundRole: if _type == "bool": if result in (True, "1"): result = QtGui.QBrush(QtCore.Qt.black) else: result = QtGui.QBrush(QtCore.Qt.white) else: if res_color_function and len(res_color_function) and res_color_function[1] != "": color_ = QtGui.QColor(res_color_function[1]) style_ = getattr(QtCore.Qt, res_color_function[2], None) result = QtGui.QBrush(color_) result.setStyle(style_) else: result = None return result # else: # print("role desconocido", role) return None
def cargarConfiguracion(self) -> None: """Load configuration.""" w = self.w_ w.findChild(QtWidgets.QWidget, u"cbFLTableDC").setChecked( self.leerValorLocal("FLTableDoubleClick") ) w.findChild(QtWidgets.QWidget, u"cbFLTableSC").setChecked( self.leerValorLocal("FLTableShortCut") ) w.findChild(QtWidgets.QWidget, u"cbFLTableCalc").setChecked( self.leerValorLocal("FLTableExport2Calc") ) w.findChild(QtWidgets.QWidget, u"cbDebuggerMode").setChecked( self.leerValorLocal("isDebuggerMode") ) w.findChild(QtWidgets.QWidget, u"cbSLConsola").setChecked(self.leerValorLocal("SLConsola")) w.findChild(QtWidgets.QWidget, u"leCallFunction").setText( self.leerValorLocal("ebCallFunction") ) w.findChild(QtWidgets.QWidget, u"leMaxPixImages").setText( self.leerValorLocal("maxPixImages") ) w.findChild(QtWidgets.QWidget, u"leNombreVertical").setText( self.leerValorGlobal("verticalName") ) w.findChild(QtWidgets.QWidget, u"cbFLLarge").setChecked( self.leerValorGlobal("FLLargeMode") == "True" ) w.findChild(QtWidgets.QWidget, u"cbPosInfo").setChecked( self.leerValorGlobal("PosInfo") == "True" ) w.findChild(QtWidgets.QWidget, u"cbMobile").setChecked(self.leerValorLocal("mobileMode")) w.findChild(QtWidgets.QWidget, u"cbDeleteCache").setChecked( self.leerValorLocal("deleteCache") ) w.findChild(QtWidgets.QWidget, u"cbParseProject").setChecked( self.leerValorLocal("parseProject") ) w.findChild(QtWidgets.QWidget, u"cbActionsMenuRed").setChecked( self.leerValorLocal("ActionsMenuRed") ) w.findChild(QtWidgets.QWidget, u"cbSpacerLegacy").setChecked( self.leerValorLocal("spacerLegacy") ) w.findChild(QtWidgets.QWidget, u"cbParseModulesOnLoad").setChecked( self.leerValorLocal("parseModulesOnLoad") ) w.findChild(QtWidgets.QWidget, u"cb_traducciones").setChecked( self.leerValorLocal("translations_from_qm") ) w.findChild(QtWidgets.QWidget, "le_temporales").setText(self.leerValorLocal("temp_dir")) w.findChild(QtWidgets.QWidget, "cb_kut_debug").setChecked( self.leerValorLocal("kugar_debug_mode") ) w.findChild(QtWidgets.QWidget, "cb_no_borrar_cache").setChecked( self.leerValorLocal("keep_general_cache") ) w.findChild(QtWidgets.QWidget, "cb_snapshot").setChecked( self.leerValorLocal("show_snaptshop_button") ) w.findChild(QtWidgets.QWidget, "cb_imagenes").setChecked( self.leerValorLocal("no_img_cached") ) w.findChild(QtWidgets.QWidget, "cb_dbadmin").setChecked( self.leerValorLocal("dbadmin_enabled") ) w.findChild(QtWidgets.QWidget, "cb_orm_enabled").setChecked( self.leerValorLocal("orm_enabled") ) w.findChild(QtWidgets.QWidget, "cb_disable_mtdparser").setChecked( self.leerValorLocal("orm_parser_disabled") ) w.findChild(QtWidgets.QWidget, "cb_disable_orm_load").setChecked( self.leerValorLocal("orm_load_disabled") ) autoComp = self.leerValorLocal("autoComp") if not autoComp or autoComp == "OnDemandF4": autoComp = "Bajo Demanda (F4)" elif autoComp == "NeverAuto": autoComp = "Nunca" else: autoComp = "Siempre" w.findChild(QtWidgets.QWidget, u"cbAutoComp").setCurrentText = autoComp w.findChild(QtWidgets.QWidget, u"leCO").hide() self.colorActual_ = self.leerValorLocal("colorObligatorio") if not self.colorActual_: self.colorActual_ = "#FFE9AD" w.findChild(QtWidgets.QWidget, u"leCO").setStyleSheet( "background-color:" + self.colorActual_ ) # Actualizaciones. from pineboolib.core.utils.utils_base import filedir import os if os.path.exists(filedir("../.git")): w.findChild(QtWidgets.QWidget, "cb_git_activar").setChecked( self.leerValorLocal("git_updates_enabled") ) ruta = self.leerValorLocal("git_updates_repo") if ruta is False: ruta = "https://github.com/Aulla/pineboo.git" w.findChild(QtWidgets.QWidget, "le_git_ruta").setText(ruta) self.module_connect( w.findChild(QtWidgets.QWidget, "pb_git_test"), u"clicked()", self, "search_git_updates", ) else: w.findChild(QtWidgets.QWidget, "tbwLocales").setTabEnabled(5, False) w.findChild(QtWidgets.QWidget, u"leCO").show()
def createUI( file_name: str, connection: Optional["iconnection.IConnection"] = None, parent: Optional["QtWidgets.QWidget"] = None, ) -> "QtWidgets.QWidget": """ Create a form from its description file. Use the FLManagerModules :: contentCached () method to get the XML text it describes the formula. @param file_name Name of the file that contains the description of the form. @param parent. Parent widget @return QWidget corresponding to the built form. """ if ".ui" not in file_name: file_name += ".ui" form_path = file_name if os.path.exists(file_name) else path._path( file_name) conn_manager = application.PROJECT.conn_manager if "main_conn" in conn_manager.connections_dict.keys(): mng_modules = conn_manager.managerModules() if mng_modules.static_db_info_ and mng_modules.static_db_info_.enabled_: ret_ui = mng_modules.contentStatic(file_name, True) if ret_ui is not None: form_path = ret_ui if form_path is None: # raise AttributeError("File %r not found in project" % n) logger.debug("createUI: No se encuentra el fichero %s", file_name) return QtWidgets.QWidget() tree = utils_base.load2xml(form_path) if not tree: return parent or QtWidgets.QWidget() root_ = tree.getroot() UIVersion = root_.get("version") if UIVersion is None: UIVersion = "1.0" if parent is None: wid = root_.find("widget") if wid is None: raise Exception( "No parent provided and also no <widget> found") xclass = wid.get("class") if xclass is None: raise Exception("class was expected") if UIVersion < "4.0": if xclass == "QMainWindow": parent = qmainwindow.QMainWindow() elif xclass in ["QDialog", "QWidget"]: parent = qdialog.QDialog() else: if xclass == "QMainWindow": parent = QtWidgets.QMainWindow() elif xclass in ["QDialog", "QWidget"]: parent = QtWidgets.QDialog() if parent is None: raise Exception("xclass not found %s" % xclass) if hasattr(parent, "widget"): w_ = parent.widget # type: ignore [attr-defined] # noqa F821 else: w_ = parent logger.info("Procesando %s (v%s)", file_name, UIVersion) if UIVersion < "4.0": qt3ui.load_ui(form_path, w_) else: from PyQt5 import uic # type: ignore qtWidgetPlugings = utils_base.filedir("plugins/custom_widgets") if qtWidgetPlugings not in uic.widgetPluginPath: logger.warning("Añadiendo path %s a uic.widgetPluginPath", qtWidgetPlugings) uic.widgetPluginPath.append(qtWidgetPlugings) uic.loadUi(form_path, w_) return w_
def contentCached(self, file_name: str, sha_key=None) -> Optional[str]: """ Get the contents of a file, using the memory and disk cache. This method first looks for the content of the requested file in the Internal cache, if not, you get it with the FLManagerModules :: content () method. @param file_name File name. @return QString with the contents of the file or None in case of error. """ sys_table: bool = False if file_name.endswith(".mtd"): if file_name[0:3] == "sys": sys_table = True else: sys_table = self.conn_.connManager().manager().isSystemTable( file_name) if not sys_table and self.static_db_info_ and self.static_db_info_.enabled_: str_ret = self.contentStatic(file_name) if str_ret is not None: return str_ret if file_name in self.filesCached_.keys(): return self.filesCached_[file_name] data = None modId: str name_: str = file_name[:file_name.index(".")] ext_: str = file_name[file_name.index(".") + 1:] type_: Optional[str] = None if ext_ == "kut": type_ = "reports/" elif ext_ == "qs": type_ = "scritps/" elif ext_ == "mtd": type_ = "tables/" elif ext_ == "ui": type_ = "forms/" elif ext_ == "qry": type_ = "queries/" elif ext_ == "ts": type_ = "translations/" elif ext_ == "xml": type_ = "" if sys_table: modId = "sys" else: modId = self.idModuleOfFile(file_name) if not sha_key: cursor = self.conn_.execute_query( "SELECT sha FROM flfiles WHERE nombre='%s'" % file_name) for contenido in cursor: sha_key = contenido[0] # if not PROJECT._DGI: # raise Exception("DGI not loaded") # if PROJECT.DGI.alternative_content_cached(): # data = project.DGI.content_cached( # project.tmpdir, self.conn_.DBName(), modId, ext_, name_, sha_key # ) # if data is not None: # return data if data is None: """Ruta por defecto""" if os.path.exists("%s/cache/%s/%s/file.%s/%s" % (application.PROJECT.tmpdir, self.conn_.DBName(), modId, ext_, name_)): utf8_ = True if ext_ == "kut" else False data = self.contentFS( "%s/cache/%s/%s/file.%s/%s/%s.%s" % ( application.PROJECT.tmpdir, self.conn_.DBName(), modId, ext_, name_, sha_key, ext_, ), utf8_, ) if data is None: if os.path.exists( utils_base.filedir("./system_module/%s%s.%s" % (type_, name_, ext_))): data = self.contentFS( utils_base.filedir("./system_module/%s%s.%s" % (type_, name_, ext_))) else: data = self.content(file_name) if data is not None: self.filesCached_[file_name] = data return data
def set_current(self, new_path: Optional[str] = None) -> None: """Set new patch.""" os.chdir(new_path or filedir("."))
def loadControls(self) -> None: """Load form controls.""" if self.pushButtonCancel: self.pushButtonCancel.hide() if self.bottomToolbar and self.toolButtonClose: self.toolButtonClose.hide() self.bottomToolbar = QtWidgets.QFrame() if self.bottomToolbar is None: raise Exception("bottomToolBar is empty!") if self.iconSize is not None: self.bottomToolbar.setMinimumSize(self.iconSize) hblay = QtWidgets.QHBoxLayout() hblay.setContentsMargins(0, 0, 0, 0) hblay.setSpacing(0) hblay.addStretch() self.bottomToolbar.setLayout(hblay) self.bottomToolbar.setFocusPolicy(QtCore.Qt.NoFocus) if self.layout_ is not None: self.layout_.addWidget(self.bottomToolbar) # if self.layout: # self.layout = None # Limpiamos la toolbar sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy(0), QtWidgets.QSizePolicy.Policy(0)) sizePolicy.setHeightForWidth(True) pbSize = self.iconSize if settings.config.value("application/isDebuggerMode", False): pushButtonExport = QtWidgets.QToolButton() pushButtonExport.setObjectName("pushButtonExport") pushButtonExport.setSizePolicy(sizePolicy) pushButtonExport.setMinimumSize(pbSize) pushButtonExport.setMaximumSize(pbSize) pushButtonExport.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-properties.png"))) pushButtonExport.setShortcut(QtGui.QKeySequence(self.tr("F3"))) pushButtonExport.setWhatsThis( QtWidgets.QApplication.translate("FLFormDB", "Exportar a XML(F3)")) pushButtonExport.setToolTip( QtWidgets.QApplication.translate("FLFormDB", "Exportar a XML(F3)")) pushButtonExport.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(pushButtonExport) pushButtonExport.clicked.connect(self.exportToXml) if settings.config.value("ebcomportamiento/show_snaptshop_button", False): push_button_snapshot = QtWidgets.QToolButton() push_button_snapshot.setObjectName("pushButtonSnapshot") push_button_snapshot.setSizePolicy(sizePolicy) push_button_snapshot.setMinimumSize(pbSize) push_button_snapshot.setMaximumSize(pbSize) push_button_snapshot.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-paste.png"))) push_button_snapshot.setShortcut( QtGui.QKeySequence(self.tr("F8"))) push_button_snapshot.setWhatsThis("Capturar pantalla(F8)") push_button_snapshot.setToolTip("Capturar pantalla(F8)") push_button_snapshot.setFocusPolicy(QtCore.Qt.NoFocus) self.bottomToolbar.layout().addWidget(push_button_snapshot) push_button_snapshot.clicked.connect(self.saveSnapShot) spacer = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) self.bottomToolbar.layout().addItem(spacer) if not self.pushButtonCancel: self.pushButtonCancel = QtWidgets.QToolButton() self.pushButtonCancel.setObjectName("pushButtonCancel") cast(QtCore.pyqtSignal, self.pushButtonCancel.clicked).connect( cast(Callable, self.close)) self.pushButtonCancel.setSizePolicy(sizePolicy) self.pushButtonCancel.setMaximumSize(pbSize) self.pushButtonCancel.setMinimumSize(pbSize) self.pushButtonCancel.setIcon( QtGui.QIcon( utils_base.filedir("./core/images/icons", "gtk-stop.png"))) # self.pushButtonCancel.setFocusPolicy(QtCore.Qt.StrongFocus) # self.pushButtonCancel.setFocus() self.pushButtonCancel.setShortcut(QtGui.QKeySequence(self.tr("Esc"))) self.pushButtonCancel.setWhatsThis("Cerrar formulario (Esc)") self.pushButtonCancel.setToolTip("Cerrar formulario (Esc)") self.bottomToolbar.layout().addWidget(self.pushButtonCancel) self.setFocusPolicy(QtCore.Qt.NoFocus)
def installPrefix(self) -> str: """Get folder where app is installed.""" return filedir("..")