def searchFile(file, subdir=None, extraDir=None): tests = [] if Settings.value('koo.custom_ui_dir'): tests += [Settings.value('koo.custom_ui_dir')] if extraDir: tests += [extraDir] if subdir: tests += [os.path.join(x, subdir) for x in sys.path] tests += [os.path.join(x, 'Koo', subdir) for x in sys.path] # The following line is needed for Koo to work properly # under windows. Mainly we say attach 'share/koo/subdir' to # sys.path, which by default has 'c:\python25' (among others). # This will give 'c:\python25\share\koo\ui' for example, which is # where '.ui' files are stored under the Windows platform. tests += [os.path.join(x, 'share', 'Koo', subdir) for x in sys.path] tests += ['%s/share/Koo/%s' % (sys.prefix, subdir)] else: tests += [os.path.join(x, 'Koo') for x in sys.path] tests += sys.path for p in tests: x = os.path.join(p, file) if os.path.exists(x): return x # Previously we returned False but None is more appropiate # and even some functions (such as initializeTranslations using # gettext.translation() will depend on it). return None
def __init__(self, url): Connection.__init__(self, url) self.url += '/rpc' from Koo.Common.Settings import Settings Pyro.config.PYRO_TRACELEVEL = int(Settings.value('pyro.tracelevel')) Pyro.config.PYRO_LOGFILE = Settings.value('pyro.logfile') Pyro.config.PYRO_DNS_URI = int(Settings.value('pyro.dns_uri')) if self.url.startswith('PYROLOCSSL'): Pyro.config.PYROSSL_CERTDIR = Settings.value('pyrossl.certdir') Pyro.config.PYROSSL_CERT = Settings.value('pyrossl.cert') Pyro.config.PYROSSL_KEY = Settings.value('pyrossl.key') Pyro.config.PYROSSL_CA_CERT = Settings.value('pyrossl.ca_cert') Pyro.config.PYROSSL_POSTCONNCHECK = int( Settings.value('pyrossl.postconncheck')) try: self.proxy = Pyro.core.getProxyForURI(self.url) except SSLError as e: title = _('SSL Error') if e.message == 'No such file or directory': msg = _('Please check your SSL certificate: ') msg += e.message msg += '\n%s' % os.path.join(Pyro.config.PYROSSL_CERTDIR, Pyro.config.PYROSSL_CERT) details = traceback.format_exc() Notifier.notifyError(title, msg, details) else: raise except Exception as e: raise
def setup_logging(): """Sets up the logging. It is put in a function, so that any vars are of local scope """ global Settings log_level = Settings.value('logging.level') or logging.WARN if Settings.value('client.debug'): log_level = logging.DEBUG logging.basicConfig(level=log_level) logging.getLogger().info("Logging started, level: %d", log_level) # This logger is very noisy, silence it. uic_log = logging.getLogger('PyQt4.uic') if Settings.value('logging.uic_debug'): uic_log.setLevel(logging.DEBUG) else: uic_log.setLevel(logging.WARN)
def searchFile(file, subdir=None, extraDir=None): """ This functions searches the given file (optionally adding a subdirectory) in the possible directories it could be found. This should hide different installation and operating system directories. Making it easier to find resource files. :param file: :param subdir: :param extraDir: :return: """ tests = [] if Settings.value('koo.custom_ui_dir'): tests += [Settings.value('koo.custom_ui_dir')] if extraDir: tests += [extraDir] if subdir: tests += [os.path.join(x, subdir) for x in sys.path] tests += [os.path.join(x, 'Koo', subdir) for x in sys.path] # The following line is needed for Koo to work properly # under windows. Mainly we say attach 'share/koo/subdir' to # sys.path, which by default has 'c:\python25' (among others). # This will give 'c:\python25\share\koo\ui' for example, which is # where '.ui' files are stored under the Windows platform. tests += [os.path.join(x, 'share', 'Koo', subdir) for x in sys.path] tests += ['%s/share/Koo/%s' % (sys.prefix, subdir)] else: tests += [os.path.join(x, 'Koo') for x in sys.path] tests += sys.path for p in tests: x = os.path.join(p, file) if os.path.exists(x): return x # Previously we returned False but None is more appropiate # and even some functions (such as initializeTranslations using # gettext.translation() will depend on it). return None
def reloadConfiguration(self): Settings.loadFromFile() self.prefix = Settings.value('runner.prefix', '') self.limit = Settings.value('runner.max_results', 5) # If no 'runner' specific settings, use standard Koo defaults self.language = Settings.value('runner.language', Settings.value('client.language')) self.url = Settings.value('runner.url', Settings.value('login.url')) self.database = Settings.value('runner.db', Settings.value('login.db')) Localization.initializeTranslations(self.language) if not Rpc.session.login(self.url, self.database): print('### KooRunner: Could not log in.')
def create(parent, definition, model): if not definition: # If definition is not set we initialize it appropiately # to be able to add the 'Print Screen' action. definition = {'print': [], 'action': [], 'relate': []} # We always add the 'Print Screen' action. definition['print'].append({ 'name': 'Print Screen', 'string': _('Print Screen'), 'report_name': 'printscreen.list', 'type': 'ir.actions.report.xml' }) actions = [] auto_shortcuts = Settings.value('koo.auto_shortcuts', False, bool) for icontype in ('print', 'action', 'relate'): for tool in definition[icontype]: action = Action(parent) action.setIcon(QIcon(":/images/%s.png" % icontype)) action.setText(Common.normalizeLabel(tool['string'])) action.setType(icontype) action.setData(tool) action.setModel(model) number = len(actions) shortcut = 'Ctrl+' if auto_shortcuts and number > 9: shortcut += 'Shift+' number -= 10 if auto_shortcuts and number < 10: shortcut += str(number) action.setShortcut(QKeySequence(shortcut)) action.setToolTip(action.text() + ' (%s)' % shortcut) actions.append(action) plugs = Plugins.list(model) for p in sorted(plugs.keys(), key=lambda x: plugs[x].get('string', '')): action = Action(parent) action.setIcon(QIcon(":/images/exec.png")) action.setText(unicode(plugs[p]['string'])) action.setData(p) action.setType('plugin') action.setModel(model) actions.append(action) return actions
def startRequestsTimer(self): # Every X minutes check for new requests and put the number of open # requests in the appropiate space in the status bar frequency = Settings.value( 'koo.requests_refresh_interval', 5 * 60, int) * 1000 if frequency > 0: self.requestsTimer.start(frequency) else: self.requestsTimer.stop() # Check for new Koo releases after logging in. self.checkNewRelease() # We always use the Subscriber as the class itself will handle # whether the module exists on the server or not self.subscriber = Rpc.Subscriber(Rpc.session, self) # Subscribe to changes to res.request (if 'koo' module is installed in the server). self.subscriber.subscribe( 'updated_model:res.request', self.updateRequestsStatus)
def __init__(self, parent): QWidget.__init__( self, parent ) ScreenDialogUi.__init__( self ) self.setupUi( self ) self.setMinimumWidth( 800 ) self.setMinimumHeight( 600 ) self.connect( self.pushOk, SIGNAL("clicked()"), self.accepted ) self.connect( self.pushCancel, SIGNAL("clicked()"), self.rejected ) self.group = None self.record = None self.recordId = None self.model = None self._recordAdded = False self._context = {} self._domain = [] self.devel_mode = Settings.value("client.devel_mode", False) if self.devel_mode: self.connect( self.pushDevInfo, SIGNAL("clicked()"),self.showLogs) else: self.pushDevInfo.hide()
def __init__(self, parent, model, attrs={}): AbstractFieldWidget.__init__(self, parent, model, attrs) OneToManyFieldWidgetUi.__init__(self) self.setupUi(self) self.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Expanding ) # Extra Actions self.actionDuplicate = QAction(self) self.actionDuplicate.setText( _('&Duplicate Selected Records') ) self.actionDuplicate.setIcon( QIcon( ':/images/duplicate.png' ) ) self.connect(self.actionDuplicate, SIGNAL('triggered()'), self.duplicate) self.actionBatchInsert = QAction(self) self.actionBatchInsert.setText( _('&Insert Several Records at Once') ) self.actionBatchInsert.setIcon( QIcon( ':/images/new.png' ) ) self.connect(self.actionBatchInsert, SIGNAL('triggered()'), self.batchInsert) self.actionBatchUpdate = QAction(self) self.actionBatchUpdate.setText( _('&Modify All Selected Records') ) self.actionBatchUpdate.setIcon( QIcon( ':/images/edit.png' ) ) self.connect(self.actionBatchUpdate, SIGNAL('triggered()'), self.batchUpdate) self.actionBatchUpdateField = QAction(self) self.actionBatchUpdateField.setText( _('&Modify Field of Selected Records') ) self.actionBatchUpdateField.setIcon( QIcon( ':/images/colorpicker.png' ) ) self.connect(self.actionBatchUpdateField, SIGNAL('triggered()'), self.batchUpdateField) self.actionsMenu = QMenu( self ) self.actionsMenu.addAction( self.actionDuplicate ) self.actionsMenu.addAction( self.actionBatchInsert ) self.actionsMenu.addAction( self.actionBatchUpdate ) if Settings.value('koo.enable_batch_update_field'): self.actionsMenu.addAction( self.actionBatchUpdateField ) self.pushActions.setMenu( self.actionsMenu ) #self.colors['normal'] = self.palette().color( self.backgroundRole() ) self.connect( self.pushNew, SIGNAL( "clicked()"),self.new ) self.connect( self.pushEdit,SIGNAL( "clicked()"),self.edit ) self.connect( self.pushRemove, SIGNAL( "clicked()"),self.remove ) self.connect( self.pushBack, SIGNAL( "clicked()"),self.previous ) self.connect( self.pushForward, SIGNAL( "clicked()"),self.next ) self.connect( self.pushSwitchView, SIGNAL( "clicked()"),self.switchView ) self.connect(self.screen, SIGNAL('recordMessage(int,int,int)'), self.setLabel) self.connect(self.screen, SIGNAL('activated()'), self.edit) # Create shortcuts self.scNew = QShortcut( self ) self.scNew.setKey( Shortcuts.NewInOneToMany ) self.scNew.setContext( Qt.WidgetWithChildrenShortcut ) self.connect( self.scNew, SIGNAL('activated()'), self.new ) self.scEdit = QShortcut( self ) self.scEdit.setKey( Shortcuts.EditInOneToMany ) self.scEdit.setContext( Qt.WidgetWithChildrenShortcut ) self.connect( self.scEdit, SIGNAL('activated()'), self.edit ) self.scDelete = QShortcut( self ) self.scDelete.setKey( Shortcuts.DeleteInOneToMany ) self.scDelete.setContext( Qt.WidgetWithChildrenShortcut ) self.connect( self.scDelete, SIGNAL('activated()'), self.remove ) self.scSwitchView = QShortcut( self ) self.scSwitchView.setKey( Shortcuts.SwitchViewInOneToMany ) self.scSwitchView.setContext( Qt.WidgetWithChildrenShortcut ) self.connect( self.scSwitchView, SIGNAL('activated()'), self.switchView ) # remove default menu entries because setting and getting default values # is not supported for OneToMany fields. However, other options such as # Inherit View in 'developer_mode' should be available. self.defaultMenuEntries = [] self.installPopupMenu( self.uiTitle )
def __init__(self): QMainWindow.__init__(self) KooMainWindowUi.__init__(self) self.menuBar = False self.setupUi(self) # Initialize singleton KooMainWindow.instance = self self.fixedWindowTitle = self.windowTitle() self.uiServerInformation.setText(_('Press Ctrl+O to login')) self.tabWidget = MainTabWidget(self. centralWidget()) self.tabWidget.currentChanged[int].connect(self.currentChanged) self.tabWidget.middleClicked[int].connect(self.closeTab) self.tabWidget.tabCloseRequested[int].connect(self.closeTab) self.pushClose = QToolButton(self.tabWidget) self.pushClose.setIcon(QIcon(':/images/close_tab.png')) self.pushClose.setAutoRaise(True) self.pushClose.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.pushClose.setToolTip(_('Close tab')) self.pushClose.clicked.connect(self.closeCurrentTab) self.tabWidget.setCornerWidget(self.pushClose, Qt.TopRightCorner) self.layout = self.centralWidget().layout() self.layout.setSpacing(2) self.layout.addWidget(self.tabWidget) self.layout.addWidget(self.frame) self.actionFullTextSearch.setShortcuts(['Ctrl+T', 'Ctrl+Alt+T']) self.actionClose.triggered.connect(self.closeCurrentTab) self.actionConnect.triggered.connect(self.showLoginDialog) self.actionDisconnect.triggered.connect(self.logout) self.actionSendRequest.triggered.connect(self.newRequest) self.actionReadMyRequest.triggered.connect(self.pendingRequests) self.actionWaitingRequests.triggered.connect(self.waitingRequests) self.actionNewDatabase.triggered.connect(self.createDatabase) self.actionExit.triggered.connect(self.close) self.actionFullTextSearch.triggered.connect(self.fullTextSearch) self.actionNextTab.triggered.connect(self.nextTab) self.actionPreviousTab.triggered.connect(self.previousTab) self.actionBackupDatabase.triggered.connect(self.backupDatabase) self.actionRestoreDatabase.triggered.connect(self.restoreDatabase) self.actionDropDatabase.triggered.connect(self.dropDatabase) self.actionAdminPassword.triggered.connect(self.changeAdministratorPassword) self.actionPreferences.triggered.connect(self.userPreferences) self.actionOpenMenuTab.triggered.connect(self.openMenuTab) self.actionOpenHomeTab.triggered.connect(self.openHomeTab) self.actionClearCache.triggered.connect(self.clearCache) self.actionHtmlManual.triggered.connect(self.openHtmlManual) self.actionPdfManual.triggered.connect(self.openPdfManual) self.actionDocOpenErpCom.triggered.connect(self.openDocOpenErpCom) self.actionTips.triggered.connect(self.showTipOfTheDay) self.actionShortcuts.triggered.connect(self.showShortcuts) self.actionLicense.triggered.connect(self.showLicense) self.actionAbout.triggered.connect(self.showAboutDialog) self.actionFormDesigner.triggered.connect(self.formDesigner) # Connect request buttons self.pushReadRequests.clicked.connect(self.pendingRequests) self.pushSendRequest.clicked.connect(self.newRequest) self.pushHelp.clicked.connect(self.help) if Settings.value('koo.enable_batch_update_field'): self.actionBatchUpdateField.setVisible(True) # These actions are not handled by the Main Window but by the currently opened tab. # What we do here, is connect all these actions to a single handler that will # call the current child/tab/form. This is handled this way instead of signals because we # may have several windows opened at the same time and all children would receive # the signal... self.actions = [ 'New', 'Save', 'Delete', 'Find', 'Previous', 'Next', 'Reload', 'Switch', 'Attach', 'Export', 'Import', 'GoToResourceId', 'Duplicate', 'AccessLog', 'BatchInsert', 'BatchUpdate', 'BatchUpdateField', 'BatchButton', 'StoreViewSettings' ] for x in self.actions: action = eval('self.action' + x) action.triggered.connect(self.callChildView) self.pushSwitchView = self.uiToolBar.widgetForAction(self.actionSwitch) self.pushSwitchView.setPopupMode(QToolButton.MenuButtonPopup) self.updateEnabledActions() self.shortcutActions = [] self.shortcutsGroup = None # Stores the id of the menu action. This is to avoid opening two menus # when 'action_id' returns the same as 'menu_id' self.menuId = False # Update the number of pending requests in the status bar using a timer but also # subscribing to model changes if 'koo' module is installed in the server. self.requestsTimer = QTimer() self.requestsTimer.timeout.connect(self.updateRequestsStatus) self.pendingRequests = -1 # Subscriber will be used to update requests status too. Only if 'koo' model is installed # on the server. self.subscriber = None # System Tray self.actionOpenPartnersTab = QAction(self) self.actionOpenPartnersTab.setIcon(QIcon(':/images/partner.png')) self.actionOpenPartnersTab.setText(_('Open partners tab')) self.actionOpenPartnersTab.triggered.connect(self.openPartnersTab) self.actionOpenProductsTab = QAction(self) self.actionOpenProductsTab.setIcon(QIcon(':/images/product.png')) self.actionOpenProductsTab.setText(_('Open products tab')) self.actionOpenProductsTab.triggered.connect(self.openProductsTab) self.systemTrayMenu = QMenu() self.systemTrayMenu.addAction(self.actionFullTextSearch) self.systemTrayMenu.addAction(self.actionOpenPartnersTab) self.systemTrayMenu.addAction(self.actionOpenProductsTab) self.systemTrayMenu.addAction(self.actionOpenMenuTab) self.systemTrayMenu.addSeparator() self.systemTrayMenu.addAction(self.actionSendRequest) self.systemTrayMenu.addAction(self.actionReadMyRequest) self.systemTrayMenu.addAction(self.actionWaitingRequests) self.systemTrayMenu.addSeparator() self.systemTrayMenu.addAction(self.actionExit) self.systemTrayIcon = QSystemTrayIcon(self) self.systemTrayIcon.setIcon(QIcon(":/images/koo-icon.png")) self.systemTrayIcon.setContextMenu(self.systemTrayMenu) self.systemTrayIcon.activated[QSystemTrayIcon.ActivationReason].connect(self.systemTrayIconActivated) if RemoteHelp.isRemoteHelpAvailable(): # Add Remote Help menu option under Windows platforms only. self.actionRemoteHelp = QAction(self) self.actionRemoteHelp.setIcon(QIcon(':/images/partner.png')) self.actionRemoteHelp.setText(_('Remote Help')) self.actionRemoteHelp.triggered.connect(self.remoteHelp) self.menuHelp.addAction(self.actionRemoteHelp) self.companyMenu = QMenu(self) self.pushCompany.setMenu(self.companyMenu) self.companyMenu.aboutToShow.connect(self.updateCompanyList) # Initialize plugins: This allows some plugins (such as SerialBarcodeScanner) # to be available in the LoginDialog. Plugins.list()
def updateEnabledActions(self): if not self.menuBar: return view = self.tabWidget.currentWidget() for x in self.actions: action = eval('self.action' + x) if view and x in view.handlers: action.setEnabled(True) else: action.setEnabled(False) if Rpc.session.open: self.actionFullTextSearch.setEnabled(True) else: self.actionFullTextSearch.setEnabled(False) if Settings.value('koo.allow_massive_updates', True): self.actionBatchInsert.setVisible(True) self.actionBatchUpdate.setVisible(True) self.actionBatchButton.setVisible(True) else: self.actionBatchInsert.setVisible(False) self.actionBatchUpdate.setVisible(False) self.actionBatchButton.setVisible(False) if Settings.value('koo.allow_import_export', True): self.actionImport.setVisible(True) self.actionExport.setVisible(True) else: self.actionImport.setVisible(False) self.actionExport.setVisible(False) # Update the 'Reports', 'Actions' and 'Browse' Menu entries self.menuReports.clear() self.menuBrowse.clear() self.menuActions.clear() self.menuPlugins.clear() reports = False browse = False actions = False plugins = False if view: self.pushSwitchView.setMenu(view.switchViewMenu()) for x in view.actions(): if x.type() == 'print': self.menuReports.addAction(x) reports = True elif x.type() == 'relate': self.menuBrowse.addAction(x) browse = True elif x.type() == 'action': self.menuActions.addAction(x) actions = True else: # Should be 'plugin' self.menuPlugins.addAction(x) plugins = True self.menuReports.setEnabled(reports) self.menuBrowse.setEnabled(browse) self.menuActions.setEnabled(actions) self.menuPlugins.setEnabled(plugins) value = Rpc.session.logged() self.actionOpenMenuTab.setEnabled(value) self.actionOpenHomeTab.setEnabled(value) self.actionPreferences.setEnabled(value) self.actionClearCache.setEnabled(value) self.actionSendRequest.setEnabled(value) self.actionReadMyRequest.setEnabled(value) self.actionWaitingRequests.setEnabled(value) self.actionDisconnect.setEnabled(value) if self.tabWidget.count() > 0: value = True else: value = False self.actionClose.setEnabled(value) self.actionNextTab.setEnabled(value) self.actionPreviousTab.setEnabled(value)
def login(self, url, databaseName): # Before connecting to the specified database # try to logout from the previous one. That will allow the # user to cancel the operation if any of the tabs is modified # and it will stop requests timer and subscription. if not self.logout(): return try: loginResponse = Rpc.session.login(url, databaseName) url = QUrl(url) if loginResponse == Rpc.session.LoggedIn: # Settings.loadFromServer() if Settings.value('koo.stylesheet_code'): QApplication.instance().setStyleSheet(Settings.value('koo.stylesheet_code')) if Settings.value('koo.use_cache'): Rpc.session.cache = Rpc.Cache.ActionViewCache() else: Rpc.session.cache = None iconVisible = Settings.value('koo.show_system_tray_icon', True) self.systemTrayIcon.setVisible(iconVisible) # Start timer once settings have been loaded because # the request interval can be configured self.startRequestsTimer() self.openMenuTab() self.openHomeTab() if Settings.value('open.model'): model = Settings.value('open.model') id = Settings.value('open.id') or False mode = 'tree,form' if id: try: id = int(id) mode = 'form,tree' except: id = False pass Api.instance.createWindow( None, model, id, view_type='form', mode=mode) if Common.isQtVersion45(): self.tabWidget.setTabsClosable( Settings.value('koo.tabs_closable')) self.updateRequestsStatus() self.updateUserShortcuts() elif loginResponse == Rpc.session.Exception: QMessageBox.warning(self, _('Connection error !'), _( 'Unable to connect to the server !')) elif loginResponse == Rpc.session.InvalidCredentials: QMessageBox.warning(self, _('Connection error !'), _( 'Bad username or password !')) except Rpc.RpcException as e: if len(e.args) == 2: (e1, e2) = e.args Common.error(_('Connection Error !'), e.args[0], e.args[1]) else: Common.error(_('Connection Error !'), _( 'Error logging into database.'), e) Rpc.session.logout()
# Note that we need translations in order to parse command line arguments # because we might have to print information to the user. However, koo's # language configuration is stored in the .rc file users might provide in # the command line. # # To solve this problem we load translations twice: one before command line # parsing and another one after, with the definitive language. # # Under windows, loading language twice doesn't work, and the first one loaded # will be the one used so we first load settings from default file and registre, # then load translations based on that file, then parse command line arguments # and eventually load definitive translations (which windows will ignore silently). Settings.loadFromFile() Settings.loadFromRegistry() Localization.initializeTranslations(Settings.value('client.language')) arguments = CommandLine.parseArguments(sys.argv) Localization.initializeTranslations(Settings.value('client.language')) imports = {} from PyQt5.QtCore import * from PyQt5.QtGui import * from Koo.Common import Notifier, Common from Koo.Common import DBus # Declare notifier handlers for the whole application Notifier.errorHandler = Common.error
# Note that we need translations in order to parse command line arguments # because we might have to print information to the user. However, koo's # language configuration is stored in the .rc file users might provide in # the command line. # # To solve this problem we load translations twice: one before command line # parsing and another one after, with the definitive language. # # Under windows, loading language twice doesn't work, and the first one loaded # will be the one used so we first load settings from default file and registre, # then load translations based on that file, then parse command line arguments # and eventually load definitive translations (which windows will ignore silently). Settings.loadFromFile() Settings.loadFromRegistry() Localization.initializeTranslations(Settings.value('client.language')) arguments = CommandLine.parseArguments(sys.argv) Localization.initializeTranslations(Settings.value('client.language')) imports = {} from PyQt5.QtCore import * from PyQt5.QtGui import * from Koo.Common import Notifier, Common from Koo.Common import DBus # Declare notifier handlers for the whole application Notifier.errorHandler = Common.error Notifier.warningHandler = Common.warning
# Note that we need translations in order to parse command line arguments # because we might have to print information to the user. However, koo's # language configuration is stored in the .rc file users might provide in # the command line. # # To solve this problem we load translations twice: one before command line # parsing and another one after, with the definitive language. # # Under windows, loading language twice doesn't work, and the first one loaded # will be the one used so we first load settings from default file and registre, # then load translations based on that file, then parse command line arguments # and eventually load definitive translations (which windows will ignore silently). Settings.loadFromFile() Settings.loadFromRegistry() Localization.initializeTranslations(Settings.value('client.language')) arguments = CommandLine.parseArguments(sys.argv) Localization.initializeTranslations(Settings.value('client.language')) imports = {} from PyQt5.QtCore import * from PyQt5.QtGui import * from Koo.Common import Notifier, Common from Koo.Common import DBus # @brief The KeyPressEventFilter class provides an eventFilter that allows # removes KeyPress events and sends them only when KeyRelease is received. This can be used as a workaround # for a strange bug found when using koopda from a RDP client from a PDA.