# coding=utf-8 from __future__ import absolute_import, division, print_function import datetime as dt import os import sys import types import ctypes import ctypes.util import traceback import re import inspect import glob import importlib import functools import pyodbc import dateutil.relativedelta try: import win32com.client except ImportError: pass from .vfpdatabase import DatabaseContext SET_PROPS = { 'bell': ['ON', ''], 'century': ['OFF', 19, 67, 2029], 'compatible': ['OFF', 'PROMPT'], 'cursor': ['ON'], 'deleted': ['OFF'], 'exact': ['OFF'], 'index': [''], 'multilocks': ['OFF'], 'near': ['OFF'], 'notify': ['ON', 'ON'], 'procedure': None, 'refresh': [0, 5], 'status': ['OFF'], 'status bar': ['ON'], 'talk': ['ON'], 'unique': ['OFF'], } HOME = sys.argv[0] if not HOME: HOME = sys.executable HOME = os.path.dirname(os.path.abspath(HOME)) SEARCH_PATH = [HOME] PCOUNTS = [] _str = str try: from PySide import QtGui, QtCore QTGUICLASSES = [c[1] for c in inspect.getmembers(QtGui, inspect.isclass)] def _in_qtgui(cls): if cls in QTGUICLASSES: return True return any(_in_qtgui(c) for c in inspect.getmro(cls) if c is not cls) class HasColor(object): @property def backcolor(self): return self.palette().color(QtGui.QPalette.Background) @backcolor.setter def backcolor(self, color): palette = self.palette() palette.setColor(self.backgroundRole(), color) self.setPalette(palette) @property def forecolor(self): return self.palette().color(QtGui.QPalette.Foreground) @forecolor.setter def forecolor(self, color): palette = self.palette() palette.setColor(self.foregroundRole(), color) self.setPalette(palette) class HasFont(object): @property def fontname(self): return self.font().family() @fontname.setter def fontname(self, val): font = self.font() font.setFamily(val) self.setFont(font) @property def fontbold(self): return self.font().bold() @fontbold.setter def fontbold(self, val): font = self.font() font.setBold(val) self.setFont(font) @property def fontitalic(self): return self.font().italic() @fontitalic.setter def fontitalic(self, val): font = self.font() font.setItalic(val) self.setFont(font) @property def fontstrikethru(self): return self.font().strikeOut() @fontstrikethru.setter def fontstrikethru(self, val): font = self.font() font.setStrikeOut(val) self.setFont(font) @property def fontunderline(self): return self.font().fontunderline() @fontunderline.setter def fontunderline(self, val): font = self.font() font.setUnderline(val) self.setFont(font) @property def fontsize(self): return self.font().pointSize() @fontsize.setter def fontsize(self, val): font = self.font() font.setPointSize(val) self.setFont(font) class Custom(object): def __init__(self, *args, **kwargs): self._assign() for key in kwargs: setattr(self, key, kwargs[key]) if _in_qtgui(type(self)) and 'parent' in kwargs: self.parent.add_object(self) self.init(*args) def init(self, *args, **kwargs): pass def add_object(self, obj): try: self.addWidget(obj) except: pass def addobject(self, name, *args): name = name.lower() setattr(self, name, create_object(*args, name=name, parent=self)) @property def parentform(self): try: t = self while not isinstance(t, (Form, Toolbar)): t = t.parent return t except: raise _EXCEPTION('object is not a member of a form') def _assign(self): pass class MainWindow(QtGui.QMainWindow): def __init__(self): super(type(self), self).__init__() self.mdiarea = QtGui.QMdiArea() self.mdiarea.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding); self.setCentralWidget(self.mdiarea) def add_object(self, obj): if isinstance(obj, Toolbar): self.addToolBar(obj) else: self.mdiarea.addSubWindow(obj) @property def caption(self): return self.windowTitle() @caption.setter def caption(self, val): self.setWindowTitle(val) @property def height(self): return QtGui.QMainWindow.height(self) @height.setter def height(self, val): self.setFixedHeight(val) @property def icon(self): return self.windowIcon() @icon.setter def icon(self, iconfile): self.setWindowIcon(QtGui.QIcon(iconfile)) class Formset(Custom): pass class Form(QtGui.QMdiSubWindow, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QMdiSubWindow.__init__(self) self._centralwidget = QtGui.QWidget(self) self._vbox = QtGui.QVBoxLayout() self._centralwidget.setLayout(self._vbox) self.setWidget(self._centralwidget) Custom.__init__(self, *args, **kwargs) def addWidget(self, obj): self._vbox.addWidget(obj) def release(self): self.close() def destroy(self): pass def closeEvent(self, event): self.destroy() @property def caption(self): return self.windowTitle() @caption.setter def caption(self, val): self.setWindowTitle(val) @property def height(self): return self._centralwidget.height() @height.setter def height(self, val): self._centralwidget.setFixedHeight(val) @property def width(self): return self._centralwidget.width() @height.setter def width(self, val): self._centralwidget.setFixedWidth(val) @property def icon(self): return self.windowIcon() @icon.setter def icon(self, iconfile): self.setWindowIcon(QtGui.QIcon(iconfile)) class Commandbutton(QtGui.QPushButton, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QPushButton.__init__(self) Custom.__init__(self, *args, **kwargs) self.clicked.connect(self.click) def click(self): pass @property def caption(self): return self.text() @caption.setter def caption(self, val): self.setText(val) @property def height(self): return QtGui.QPushButton.height(self) @height.setter def height(self, val): self.setFixedHeight(val) @property def width(self): return QtGui.QPushButton.width(self) @height.setter def width(self, val): self.setFixedWidth(val) @property def enabled(self): return self.isEnabled() @enabled.setter def enabled(self, val): self.setEnabled(val) @property def visible(self): return self.isVisible() @visible.setter def visible(self, val): self.setVisible(val) @property def picture(self): return QtGui.QPushButton.icon(self) @picture.setter def picture(self, iconfile): self.setIcon(QtGui.QIcon(iconfile)) @property def tooltiptext(self): return self.toolTip() @tooltiptext.setter def tooltiptext(self, val): self.setToolTip(val) @property def specialeffect(self): if self.isFlat(): return 2 else: return 0 @specialeffect.setter def specialeffect(self, val): self.setFlat(val == 2) class Label(QtGui.QLabel, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QLabel.__init__(self) Custom.__init__(self, *args, **kwargs) @property def caption(self): return self.text() @caption.setter def caption(self, val): self.setText(val) @property def height(self): return QtGui.QLabel.height(self) @height.setter def height(self, val): self.setFixedHeight(val) @property def width(self): return QtGui.QLabel.width(self) @height.setter def width(self, val): self.setFixedWidth(val) class Textbox(QtGui.QLineEdit, Custom, HasColor, HasFont): def __init__(self, *args, **kwargs): QtGui.QLineEdit.__init__(self) Custom.__init__(self, *args, **kwargs) @property def height(self): return QtGui.QLabel.height(self) @height.setter def height(self, val): self.setFixedHeight(val) @property def value(self): return self.text() @value.setter def value(self, val): self.setText(val) @property def visible(self): return self.isVisible() @visible.setter def visible(self, val): self.setVisible(val) def focusInEvent(self, event): self.gotfocus() def focusOutEvent(self, event): self.lostfocus() def setfocus(self): self.setFocus() class Checkbox(QtGui.QCheckBox, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QCheckBox.__init__(self) Custom.__init__(self, *args, **kwargs) self.clicked.connect(self.click) self.clicked.connect(self.interactivechange) def click(self): pass def interactivechange(self): pass @property def caption(self): return self.text() @caption.setter def caption(self, val): self.setText(val) @property def height(self): return QtGui.QLabel.height(self) @height.setter def height(self, val): self.setFixedHeight(val) class Combobox(QtGui.QComboBox, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QComboBox.__init__(self) Custom.__init__(self, *args, **kwargs) self.currentIndexChanged.connect(self.interactivechange) def additem(self, val): index = self.currentIndex() self.blockSignals(True) self.addItem(val) self.setCurrentIndex(index) self.blockSignals(False) @property def value(self): return self.currentText() @value.setter def value(self, val): index = self.findText(val, QtCore.Qt.MatchFixedString) self.blockSignals(True) self.setCurrentIndex(index) self.blockSignals(False) self.programmaticchange() def programmaticchange(self): pass def interactivechange(self): pass @property def caption(self): return self.text() @caption.setter def caption(self, val): self.setText(val) @property def height(self): return QtGui.QComboBox.height(self) @height.setter def height(self, val): self.setFixedHeight(val) @property def width(self): return QtGui.QComboBox.width(self) @height.setter def width(self, val): self.setFixedWidth(val) @property def style(self): if self.isEditable(): return 0 else: return 2 @style.setter def style(self, val): self.setEditable(val == 0) class Spinner(QtGui.QSpinBox, Custom, HasFont, HasColor): def __init__(self, *args, **kwargs): QtGui.QSpinBox.__init__(self) Custom.__init__(self, *args, **kwargs) self.pressed = False self.installEventFilter(self) self.valueChanged.connect(self.interactivechange) def eventFilter(self, widget, event): if event.type() == QtCore.QEvent.Type.MouseButtonPress: self.pressed = True if event.type() == QtCore.QEvent.Type.MouseButtonRelease: if self.pressed: self.click() self.pressed = False return QtGui.QWidget.eventFilter(self, widget, event) def click(self): pass def interactivechange(self): pass def programmaticchange(self): pass @property def value(self): return QtGui.QSpinBox.value(self) @value.setter def value(self, val): self.blockSignals(True) self.setValue(val) self.blockSignals(False) self.programmaticchange() class Shape(QtGui.QFrame, Custom): def __init__(self, *args, **kwargs): QtGui.QFrame.__init__(self) Custom.__init__(self, *args, **kwargs) self.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain) @property def height(self): return QtGui.QComboBox.height(self) @height.setter def height(self, val): self.setFixedHeight(val) @property def width(self): return QtGui.QComboBox.width(self) @height.setter def width(self, val): self.setFixedWidth(val) class Separator(QtGui.QFrame, Custom): def __init__(self, *args, **kwargs): QtGui.QFrame.__init__(self) Custom.__init__(self, *args, **kwargs) self.setFrameStyle(QtGui.QFrame.VLine | QtGui.QFrame.Plain) palette = self.palette() palette.setColor(self.foregroundRole(), QtGui.QColor(0, 0, 0, 0)) self.setPalette(palette) margin = 4 self.setContentsMargins(margin, 0, margin, 0) class Toolbar(QtGui.QToolBar, Custom): def __init__(self, *args, **kwargs): QtGui.QToolBar.__init__(self) Custom.__init__(self, *args, **kwargs) class Listbox(QtGui.QListWidget, Custom): def __init__(self, *args, **kwargs): QtGui.QListWidget.__init__(self) self.multiselect = 0 Custom.__init__(self, *args, **kwargs) self._source = '' @property def rowsource(self): return self._source @rowsource.setter def rowsource(self, source): self._source = source table = DB._get_table_info(source).table for record in table: self.addItem(QtGui.QListWidgetItem(_str(record[0]))) @property def multiselect(self): return int(self.selectionMode() == QtGui.QAbstractItemView.MultiSelection) @multiselect.setter def multiselect(self, mode): self.setSelectionMode(QtGui.QAbstractItemView.MultiSelection if mode else QtGui.QAbstractItemView.SingleSelection) class Grid(QtGui.QTableWidget, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QTableWidget.__init__(self) Custom.__init__(self, *args, **kwargs) self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self._source = '' self.cellClicked.connect(self._update_recno) self.cellClicked.connect(self.click) self.cellDoubleClicked.connect(self.dblclick) def _update_recno(self): table = DB._get_table_info(self._source).table.goto(self.currentRow()) self.refresh() def click(self): pass def dblclick(self): pass def refresh(self): table = DB._get_table_info(self._source).table labels = table.field_names self.setColumnCount(len(labels)) self.setRowCount(len(table)) self.setVerticalHeaderLabels(['>' if record is table.current_record else ' ' for record in table]) self.setHorizontalHeaderLabels(labels) for i, record in enumerate(table): for j, val in enumerate(record): self.setItem(i, j, QtGui.QTableWidgetItem(_str(val))) def focusInEvent(self, event): self.refresh() @property def allowrowsizing(self): return self.verticalHeader().resizeMode(0) == QtGui.QHeaderView.Interactive @allowrowsizing.setter def allowrowsizing(self, val): self.verticalHeader().setResizeMode(QtGui.QHeaderView.Interactive if val else QtGui.QHeaderView.Fixed) @property def recordsource(self): return self._source @recordsource.setter def recordsource(self, source): self._source = source self.refresh() @property def allowcellselection(self): return self.selectionBehavior() != QtGui.QAbstractItemView.SelectItems @allowcellselection.setter def allowcellselection(self, mode): self.setSelectionBehavior(QtGui.QAbstractItemView.SelectItems if mode else QtGui.QAbstractItemView.SelectRows) self.setEditTriggers(QtGui.QAbstractItemView.AllEditTriggers if mode else QtGui.QAbstractItemView.NoEditTriggers) class Optiongroup(QtGui.QWidget, Custom): def __init__(self, *args, **kwargs): QtGui.QWidget.__init__(self) self._group = QtGui.QButtonGroup() self._vbox = QtGui.QVBoxLayout() self.setLayout(self._vbox) Custom.__init__(self, *args, **kwargs) def add_object(self, obj): self._vbox.addWidget(obj) self._group.addButton(obj) @property def enabled(self): return any(button.enabled for button in self._group.buttons()) @enabled.setter def enabled(self, val): for button in self._group.buttons(): button.enabled = val @property def buttoncount(self): return len(self._group.buttons()) @buttoncount.setter def buttoncount(self, val): for i in range(val, self.buttoncount): self._group.buttons()[-1].deleteLater() for i in range(self.buttoncount, val): caption = 'option {}'.format(i + 1) name = caption.replace(' ', '') setattr(self, name, Optionbutton(caption=caption, name=name, parent=self)) class Optionbutton(QtGui.QRadioButton, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QRadioButton.__init__(self) Custom.__init__(self, *args, **kwargs) @property def caption(self): return self.text() @caption.setter def caption(self, val): self.setText(val) @property def enabled(self): return self.isEnabled() @enabled.setter def enabled(self, val): self.setEnabled(val) class Pageframe(QtGui.QTabWidget, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QTabWidget.__init__(self) Custom.__init__(self, *args, **kwargs) def addWidget(self, widget): self.addTab(widget, widget.caption) class Page(QtGui.QWidget, Custom, HasFont): def __init__(self, *args, **kwargs): QtGui.QWidget.__init__(self) self._vbox = QtGui.QVBoxLayout() self.setLayout(self._vbox) self.caption = kwargs['name'] Custom.__init__(self, *args, **kwargs) def addWidget(self, widget): self._vbox.addWidget(widget) class Image(Custom): pass class Timer(Custom): pass class Container(Custom): pass class Editbox(Custom): pass class Dataenvironment(Custom): pass qt_app = QtGui.QApplication(()) def _exec(): S['_screen'].show() return qt_app.exec_() except ImportError: class Custom(object): pass class MainWindow(Custom): pass class Formset(Custom): pass class Form(Custom): pass class Commandbutton(Custom): pass class Label(Custom): pass class Textbox(Custom): pass class Checkbox(Custom): pass class Combobox(Custom): pass class Spinner(Custom): pass class Shape(Custom): pass class Separator(Custom): pass class Toolbar(Custom): pass class Listbox(Custom): pass class Grid(Custom): pass class Optiongroup(Custom): pass class Optionbutton(Custom): pass class Pageframe(Custom): pass class Page(Custom): pass class Image(Custom): pass class Timer(Custom): pass class Container(Custom): pass class Editbox(Custom): pass class Dataenvironment(Custom): pass _EXCEPTION = Exception class Exception(object): def __init__(self): self.message = '' self.lineno = 0 self.errorno = 0 self.procedure = '' self.stacklevel = 0 self.linecontents = '' @classmethod def from_pyexception(cls, exc): trace = traceback.extract_stack() obj = cls() obj.message = _str(exc) if hasattr(exc, '__traceback__'): tb = exc.__traceback__ else: tb = sys.exc_info()[2] obj.lineno = tb.tb_lineno filename = os.path.splitext(os.path.basename(tb.tb_frame.f_code.co_filename))[0] obj.procedure = filename + '.' + tb.tb_frame.f_code.co_name obj.stacklevel = len(trace) - 1 return obj class Array(object): def __init__(self, dim1, dim2=0, offset=0): self.columns = bool(dim2) self.offset = offset if not dim2: dim2 = 1 self.dim1 = int(dim1) self.data = [False]*self.dim1*int(dim2) def _subarray(self, inds): def modify_slice(ind): def modify_slice_ind(slice_ind): return slice_ind - 1 if slice_ind is not None else None return slice(modify_slice_ind(ind.start), modify_slice_ind(ind.stop), ind.step) ind = modify_slice(inds) data = self.data[ind] new_array = Array(len(data), 1, ind.start) new_array.data = data return new_array def _get_index(self, inds): if not isinstance(inds, tuple): inds = (inds, 1) if len(inds) < 2: inds = (inds[0], 1) ind1, ind2 = [int(ind) - 1 for ind in inds] ind = (len(self) // self.dim1)*ind1 + ind2 - self.offset if ind < 0: raise IndexError('invalid indices') return ind def __getitem__(self, inds): if isinstance(inds, slice): return self._subarray(inds) return self.data[self._get_index(inds)] def __setitem__(self, inds, val): self.data[self._get_index(inds)] = val def __call__(self, *args): return self[args] def __len__(self): return len(self.data) def alen(self, arr_attr=0): if arr_attr == 2 and not self.columns: return 0 return { 0: len(self), 1: self.dim1, 2: len(self) // self.dim1, }[arr_attr] def index(self, val): try: return self.data.index(val) + 1 + self.offset except ValueError: return 0 def __iter__(self): return iter(self.data) def __repr__(self): dims = (self.dim1,) if self.dim1 == len(self) else (self.dim1, len(self) // self.dim1) return '{}({})'.format(type(self).__name__, ', '.join(_str(dim) for dim in dims)) class _Memvar(object): def __init__(self): self.__dict__['public_scopes'] = [] self.__dict__['local_scopes'] = [] def _get_scope(self, key): if len(self.local_scopes) > 0 and key in self.local_scopes[-1]: return self.local_scopes[-1] for scope in reversed(self.public_scopes): if key in scope: return scope def __contains__(self, key): try: self[key] return True except: return False def __getattr__(self, key): return self[key] def __getitem__(self, key): scope = self._get_scope(key) if scope is not None: return scope[key] raise NameError('name {} is not defined'.format(key)) def __setitem__(self, key, val): (self._get_scope(key) or self.public_scopes[-1])[key] = val def __setattr__(self, key, val): if key in self.__dict__: super(_Variable, self).__setattr__(key, val) else: self[key] = val def __delitem__(self, key): scope = self._get_scope(key) if scope: del scope[key] def __delattr__(self, key): del self[key] def _add_to_scopes(self, scope, *args, **kwargs): for arg in args: scope[arg] = False for key in kwargs: scope[key] = kwargs[key] def add_local(self, *args, **kwargs): self._add_to_scopes(self.local_scopes[-1], *args, **kwargs) def add_private(self, *args, **kwargs): self._add_to_scopes(self.public_scopes[-1], *args, **kwargs) def add_public(self, *args, **kwargs): self._add_to_scopes(self.public_scopes[0], *args, **kwargs) def pushscope(self): self.public_scopes.append({}) self.local_scopes.append({}) def popscope(self, *args): self.public_scopes.pop() self.local_scopes.pop() if args: return args[0] if len(args) == 1 else args def release(self, mode='Normal', skeleton=None): if mode == 'Extended': pass elif mode == 'Like': pass elif mode == 'Except': pass else: for var in list(self.local_scopes[-1]) + list(self.public_scopes[-1]): del self[var] class _Variable(object): def __init__(self, memvar, db): self.__dict__['memvar'] = memvar self.__dict__['db'] = db def __getattr__(self, key): return self[key] def __getitem__(self, key): try: return self.db._get_table_info(key).table.current_record except: pass table_info = self.db._get_table_info() if table_info is not None and key in table_info.table.field_names: return table_info.table.current_record[key] return self.memvar[key] def __setitem__(self, key, val): self.memvar[key] = val def __setattr__(self, key, val): if key in self.__dict__: super(_Variable, self).__setattr__(key, val) else: self.memvar[key] = val class _Function(object): def __init__(self): self.__dict__['modules'] = [] self.__dict__['dlls'] = [] def __getattr__(self, key): return self[key] def __getitem__(self, key): for dll in self.dlls: if hasattr(dll, key): return getattr(dll, key) for module in self.modules: if not hasattr(module, key) or (hasattr(module, '_CLASSES') and key in module._CLASSES): continue obj = getattr(module, key) if isinstance(obj, (types.FunctionType, types.BuiltinFunctionType)): return obj for scope in M.public_scopes: if key in scope and isinstance(scope[key], Array): return scope[key] raise _EXCEPTION('{} is not a procedure'.format(key)) def set_procedure(self, module, additive=False): if not additive: self.modules[:] = [] elif module in self.modules: self.modules.remove(module) self.modules.insert(0, module) def release_procedure(self, *modules): for module in modules: self.modules.remove(module) def dll_declare(self, dllname, funcname, alias): # need something here to determine more about the dll file. try: dll = ctypes.CDLL(dllname) except: dll = ctypes.CDLL(ctypes.util.find_library(dllname)) try: dll = next(d for d in self.dlls if d._name == dll._name) except: pass func = getattr(dll, funcname) alias = alias or funcname setattr(dll, alias, func) if dll not in self.dlls: self.dlls.append(dll) def dll_clear(self, *funcs): if not funcs: self.dlls = [] else: for dll in self.dlls: for func in funcs: if hasattr(dll, func): delattr(dll, func) class _Class(object): def __init__(self): self.modules = [] def __getattr__(self, key): return self[key] def __getitem__(self, key): for module in self.modules: if hasattr(module, '_CLASSES') and key in module._CLASSES: return module._CLASSES[key]() elif hasattr(module, key) and inspect.isclass(getattr(module, key)): return getattr(module, key) raise KeyError(key) def set_procedure(self, module, additive=False): if not additive: self.modules[:] = [] elif module in self.modules: self.modules.remove(module) self.modules.insert(0, module) def release_procedure(self, *modules): for module in modules: self.modules.remove(module) def atline(search, string): return string[:string.find(search)+1].count('\r') + 1 def capslock(on=None): pass def cdx(index, workarea): pass def chrtran(expr, fchrs, rchrs): return ''.join(rchrs[fchrs.index(c)] if c in fchrs else c for c in expr) def ctod(string): return dt.datetime.strptime(string, '%m/%d/%Y').date() def ddeinitiate(service, topic): import dde import win32ui try: server = win32ui.GetApp().ddeServer conn = dde.CreateConversation(server) conn.ConnectTo(service, topic) return conn except dde.error: return -1 def ddesetoption(a, b): pass def ddeterminate(a): pass def ddesetservice(a, b, c=False): pass def ddesettopic(a, b, c): pass def dow_fix(weekday, firstday=0): return (weekday + 2 - (firstday or 1)) % 7 + 1 def dtos(dateobj): fmt = '%Y%m%d' if hasattr(dateobj, 'hour'): fmt += '%H%M%S' return dateobj.strftime(fmt) def dtoc(dateobj): fmt = '%m/%d/%Y' if hasattr(dateobj, 'hour'): fmt += '%H:%M:%S' return dateobj.strftime(fmt) def error(txt): raise _EXCEPTION(txt) def fdate(filename, datetype=0): if datetype not in (0, 1): raise ValueError('datetype is invalid') mod_date = dt.datetime.fromtimestamp(os.path.getmtime(filename)) return mod_date if datetype else mod_date.date() def ftime(filename): return fdate(filename, 1).time() def filetostr(filename): with open(filename) as fid: return fid.read().decode('ISO-8859-1') def getdir(dir='.', text='', caption='Select Text', flags=0, root_only=False): return QtGui.QFileDialog.getExistingDirectory(parent=S['_screen'], caption=caption, dir=dir) def getfile(file_ext='', text='', button_caption='', button_type=0, title=''): filter = { '': '', 'txt': 'File (*.txt)', 'dbf': 'Table/DBF (*.dbf)', }.get(file_ext, '*.' + file_ext) t = QtGui.QFileDialog() t.setFilter('All Files (*.*);;' + filter) t.selectFilter(filter or 'All Files (*.*);;') if text: (next(x for x in t.findChildren(QtGui.QLabel) if x.text() == 'File &name:')).setText(text) if button_caption: t.setLabelText(QtGui.QFileDialog.Accept, button_caption) if title: t.setWindowTitle(title) t.exec_() return t.selectedFiles()[0] def _getwords(string, delim=None): return [w for w in string.split(delim) if w] def getwordcount(string, delim=None): return len(_getwords(string, delim)) def getwordnum(string, index, delim=None): str_list = _getwords(string, delim) if 0 <= index <= len(str_list): return str_list[index-1] return '' def gomonth(dateval, delta): return dateval + dateutil.relativedelta.relativedelta(months=delta) def home(location=0): if location != 0: raise _EXCEPTION('not implemented') return HOME def inkey(seconds=0, hide_cursor=False): pass def isblank(expr): try: return not expr.strip() except: return expr is None def like(matchstr, string): matchstr = matchstr.replace('.', r'\.').replace('*', '.*').replace('?', '.') return bool(re.match(matchstr, string)) def lineno(flag=None): exc_type, exc_obj, exc_tb = sys.exc_info() return exc_tb.tb_lineno def locfile(filename, ext='', caption=''): '''find file on path''' if os.path.isfile(filename): return os.path.abspath(filename) dirname = os.path.dirname(filename) filename = os.path.basename(filename) for path in SEARCH_PATH: testpath = os.path.join(path, filename) if os.path.isfile(testpath): return os.path.abspath(testpath) return getfile(file_ext=ext, title=caption) def message(flag=None): exc_type, exc_obj, exc_tb = sys.exc_info() if flag is None: return exc_obj.message return traceback.extract_tb(exc_tb)[0][3] def messagebox(msg, arg1=None, arg2=None, timeout=None, details=''): OK_ONLY = 0 OK_CANCEL = 1 ABORT_RETRY_IGNORE = 2 YES_NO_CANCEL = 3 YES_NO = 4 RETRY_CANCEL = 5 NOICON = 0 STOPSIGN = 16 QUESTION = 32 EXCLAMATION = 48 INFORMATION = 64 FIRSTBUTTON = 0 SECONDBUTTON = 256 THIRDBUTTON = 512 OK = QtGui.QMessageBox.Ok CANCEL = QtGui.QMessageBox.Cancel ABORT = QtGui.QMessageBox.Abort RETRY = QtGui.QMessageBox.Retry IGNORE = QtGui.QMessageBox.Ignore YES = QtGui.QMessageBox.Yes NO = QtGui.QMessageBox.No RETURN_OK = 1 RETURN_CANCEL = 2 RETURN_ABORT = 3 RETURN_RETRY = 4 RETURN_IGNORE = 5 RETURN_YES = 6 RETURN_NO = 7 def center_widget(widget): '''center the widget on the screen''' widget_pos = widget.frameGeometry() screen_center = QtGui.QDesktopWidget().screenGeometry().center() widget_pos.moveCenter(screen_center) widget.move(widget_pos.topLeft()) flags=0 title='vfp2py' if arg1 is not None: if isinstance(arg1, _str): title = arg1 if arg2 is not None: flags = arg2 else: flags = arg1 if arg2 is not None: title = arg2 flags = int(flags) & 1023 buttons = { OK_ONLY: ((OK,), OK), OK_CANCEL: ((OK, CANCEL), CANCEL), ABORT_RETRY_IGNORE: ((ABORT, RETRY, IGNORE), IGNORE), YES_NO_CANCEL: ((YES, NO, CANCEL), CANCEL), YES_NO: ((YES, NO), NO), RETRY_CANCEL: ((RETRY, CANCEL), CANCEL) }[flags & 15] buttonobj = buttons[0][0] for button in buttons[0][1:]: buttonobj |= button icon = { NOICON: QtGui.QMessageBox.NoIcon, STOPSIGN: QtGui.QMessageBox.Critical, QUESTION: QtGui.QMessageBox.Question, EXCLAMATION: QtGui.QMessageBox.Warning, INFORMATION: QtGui.QMessageBox.Information }[flags & (15 << 4)] default_button = min(len(buttons[0])-1, (flags >> 8)) msg_box = QtGui.QMessageBox(icon, title, msg, buttons=buttonobj) msg_box.setDefaultButton(buttons[0][default_button]) msg_box.setEscapeButton(buttons[1]) if details: msg_box.setDetailedText(details) msg_box.show() retval = [0] def closebox(): t = retval t[0] = -1 msg_box.close() if timeout: timer = QtCore.QTimer() timer.setSingleShot(True) timer.timeout.connect(closebox) timer.start(float(timeout)*1000) timeout = timer button = msg_box.exec_() if timeout and timeout.isActive(): timeout.stop() retval = retval[0] center_widget(msg_box) return { OK: RETURN_OK, CANCEL: RETURN_CANCEL, ABORT: RETURN_ABORT, RETRY: RETURN_RETRY, IGNORE: RETURN_IGNORE, YES: RETURN_YES, NO: RETURN_NO }[button] if retval != -1 else -1 def pcount(): return PCOUNTS[-1] def program(level=None): trace = traceback.extract_stack()[:-1] if level is None: level = -1 elif level < 0: return len(trace) - 1 elif level == 0: level = 1 if level >= len(trace): return '' prg = trace[level][2] if prg == '_program_main': prg = re.sub(r'.py$', '', trace[level][0]) return prg.upper() def quarter(datetime, start_month=1): if not datetime: return 0 month = (datetime.month - start_month) % 12 return (month - (month % 3)) // 3 + 1 def quit(message=None, flags=None, title=None): sys.exit() def ratline(search, string): return string[:string.rfind(search)+1].count('\r') + 1 def rgb(red, green, blue): return QtGui.QColor(red, green, blue) def seconds(): now = dt.datetime.now() return (now - dt.datetime.combine(now, dt.time(0, 0))).seconds def str(num, length=10, decimals=0): length = int(length) string = '{{:.{}f}}'.format(int(decimals)).format(num) if len(string) > length and len(string) - (decimals - 1) <= length: string = string[:length] elif len(string) > length: string = string.split('.')[0] if len(string) >= length: string = '{:E}'.format(num) groups = list(re.match(r'(\d.)(\d+)(E[+-])0*(\d+)', string).groups()) trim = sum(len(x) for x in groups) - length if trim > 0: groups[1] = groups[1][:-trim] string = ''.join(groups) string = string.rjust(length) return string if len(string) == length else '*' * length def strextract(string, begin, end='', occurance=1, flag=0): begin = re.escape(begin) end = re.escape(end) if end: between = '.*?' else: between = '.*' if flag & 4: regstr = '({}{}{})'.format(begin, between, end) else: regstr = '{}({}){}'.format(begin, between, end) try: if flag & 1: flags = re.IGNORECASE else: flags = 0 return re.findall(regstr, string, flags=flags)[occurance - 1] except: if flag & 2 and end: return strextract(string, begin, '', occurance, flag & 5) return '' def strtofile(string, filename, flag=0): flag = int(flag) if flag not in (0, 1, 2, 4): raise ValueError('invalid flag') mode = 'ab' if flag == 1 else 'wb' if flag == 2: output = string.encode('utf-16') elif flag == 4: output = b'\xef\xbb\xbf' + string.encode('utf-8') else: output = string try: with open(filename, mode) as fid: fid.write(output) return len(output) except: return 0 def strtran(string, old, new='', start=0, maxreplace=None, flags=0): retstr = '' while start > 0: try: ind = string.find(old) + len(old) retstr += string[:ind] string = string[ind:] except: break start -= 1 if maxreplace: retstr += string.replace(old, new, maxreplace) else: retstr += string.replace(old, new) return retstr def stuff(string, start, num_replaced, repl): return string[:int(start)-1] + repl + string[int(start)-1+int(num_replaced):] def sqlcommit(conn): try: conn.commit() return 1 except: return -1 def sqlconnect(name, userid=None, password=None, shared=False): return pyodbc.connect('dsn=' + name) def sqlstringconnect(*args): if len(args) == 0 or len(args) == 1 and isinstance(args[0], bool): connect_string = None #should pop up a dialog shared = args[0] if args else False else: connect_string = args[0] shared = args[1] if len(args) > 1 else False return pyodbc.connect(connect_string) def _odbc_cursor_to_db(results, cursor_name): if not cursor_name: cursor_name = 'sqlresult' try: values = results.fetchall() if not values: raise _EXCEPTION('') column_info = values[0].cursor_description columns = [] for i, column in enumerate(column_info): field_name = column[0][:10] field_type = { int: 'N', _str: 'C', bool: 'L', float: 'N', }[column[1]] if field_type == 'N': field_lens = column[4:6] elif field_type == 'L': field_lens = '' else: field_lens = '({})'.format(column[4]) columns.append('{} {}{}'.format(field_name, field_type, field_lens)) DB.use(None, DB.select_function(cursor_name), None) DB.create_table(cursor_name + '.dbf', columns, 'free') for value in values: DB.insert(cursor_name, tuple(value)) DB.goto(cursor_name, 0) except: pass def sqlexec(conn, cmd='', cursor_name='', count_info=''): try: results = conn.execute(cmd) _odbc_cursor_to_db(results, cursor_name) return 1 except: return -1 def sqlrollback(conn): try: conn.rollback() return 1 except: return -1 def sqltables(conn, table_type='', cursor_name=''): try: _odbc_cursor_to_db(conn.cursor().tables(), cursor_name) return 1 except: return -1 def sqldisconnect(connection): connection.close() SYS2000iter = None def vfp_sys(funcnum, *args): global SYS2000iter if funcnum == 16: import imp if hasattr(sys, "frozen") or hasattr(sys, "importers") or imp.is_frozen("__main__"): return os.path.dirname(sys.executable) return os.path.dirname(sys.argv[0]) if funcnum == 2000: #seems to implement FindFirstFile and FindNextFile in win32api if len(args) == 1: skel, = args next_file = False else: skel, next_file = args[:2] if next_file: try: return next(SYS2000iter) except: return '' SYS2000iter = glob.iglob(skel) return vfp_sys(2000, skel, 1) TYPE_MAP = { _str: 'C', dt.date: 'D', bool: 'L', int: 'N', float: 'N', dt.datetime: 'T', type(None): 'X', } if sys.version_info < (3,): TYPE_MAP[unicode] = 'C' def vartype(var): return TYPE_MAP.get(type(var), 'O') def version(ver_type=4): if ver_type == 4: return 'Not FoxPro 9' if ver_type == 5: return 900 def wait(msg, to=None, window=[-1, -1], nowait=False, noclear=False, timeout=-1): pass def gather(val=None, fields=None, fieldstype=None, memo=None): DB._update_from(val) def scatter(totype=None, name=None, fields=None, fieldstype=None, memo=False, blank=False): record = DB._current_record_copy() if totype == 'name': return record def set(setword, *args, **kwargs): setword = setword.lower() settings = SET_PROPS[setword] if not kwargs.get('set_value', False): return settings[args[0]] if args else settings[0] if setword == 'bell': if args[0].lower() not in ('to', 'on', 'off'): raise ValueError('Bad argument: {}'.format(args[0])) if args[0] == 'TO': settings[1] = '' if len(args) == 1 else args[1] else: settings[0] = args[0].upper() elif setword == 'century': if len(args) > 0: settings[0] = args[0] if 'century' in kwargs: settings[1] = kwargs['century'] if 'rollover' in kwargs: settings[2] = kwargs['rollover'] elif setword == 'compatible': if args[0].lower() not in ('on', 'off'): raise ValueError('Bad argument: {}'.format(args[0])) settings[0] = args[0].upper() if len(args) > 1: if args[1].lower() not in ('prompt', 'noprompt'): settings[1] = args[1].upper() elif setword == 'notify': if 'cursor' in kwargs: settings[1] = kwargs['cursor'] else: settings[0] = args[0] elif setword in ('cursor', 'deleted', 'exact', 'multilocks', 'near', 'status', 'status bar', 'talk', 'unique'): if args[0].lower() not in ('on', 'off'): raise ValueError('Bad argument: {}'.format(args[0])) settings[0] = args[0].upper() elif setword in ('index', 'refresh'): settings = args elif setword == 'procedure': module = importlib.import_module(args[0]) F.set_procedure(module, additive=kwargs.get('additive', False)) C.set_procedure(module, additive=kwargs.get('additive', False)) SET_PROPS[setword] = settings def text(text_lines, show=True): text = ''.join(l.strip() for l in text_lines) if show: print(text) return text def create_object(objtype, *args, **kwargs): objtype = objtype.title() frame = inspect.getouterframes(inspect.currentframe())[2][0] if objtype in frame.f_globals: return frame.f_globals[objtype](*args, **kwargs) try: return C[objtype]() except: pass try: return win32com.client.Dispatch(objtype) except: pass raise _EXCEPTION('class definition \'{}\' not found'.format(objtype)) def clearall(): pass def clear(*args): pass DB = DatabaseContext() M = _Memvar() S = _Variable(M, DB) F = _Function() C = _Class() def module(module_name): return __import__(module_name.lower()) error_func = None M.pushscope() M.add_public('_screen') S._screen = MainWindow() S._screen.caption = 'VFP To Python' def _parameters(scope_func, *varnames): def decorator(fn): fn_args = fn.__code__.co_varnames if len(fn_args) == 1 and fn_args[0] == 'self' and varnames: @functools.wraps(fn) def scoper(self, *args): global PARAMETERS M.pushscope() PARAMETERS = len(args) PCOUNTS.append(PARAMETERS) kwargs = {name: arg for name, arg in zip(varnames, args)} args = varnames[len(args):] scope_func(*args, **kwargs) retval = fn(self) PCOUNTS.pop() M.popscope() return retval elif varnames: @functools.wraps(fn) def scoper(*args): global PARAMETERS M.pushscope() PARAMETERS = len(args) PCOUNTS.append(PARAMETERS) kwargs = {name: arg for name, arg in zip(varnames, args)} args = varnames[len(args):] scope_func(*args, **kwargs) retval = fn() PCOUNTS.pop() M.popscope() return retval else: @functools.wraps(fn) def scoper(*args): global PARAMETERS M.pushscope() PARAMETERS = 0 PCOUNTS.append(PARAMETERS) retval = fn(*args) PCOUNTS.pop() M.popscope() return retval scoper.__name__ = fn.__name__ return scoper return decorator def parameters(*varnames): return _parameters(M.add_private, *varnames) def lparameters(*varnames): return _parameters(M.add_local, *varnames) def vfpclass(fn): if '_CLASSES' not in fn.__globals__: fn.__globals__['_CLASSES'] = {} fn.__globals__['_CLASSES'][fn.__name__] = fn @functools.wraps(fn) def double_caller(*args, **kwargs): return fn()(*args, **kwargs) fn.__globals__[fn.__name__ + 'Type'] = fn return double_caller set('procedure', 'vfp2py.vfpfunc', set_value=True)