This commit is contained in:
Nicolas Évrard committed 2012-01-27 12:27:55 +01:00
commit fc87b7ef6f
82 files changed
+1007 -927

No files matched your search

-18
View File
@@ -581,24 +581,6 @@ Tabs Position
* Right
* Bottom
.. _Menu-Options-File_Actions:
File Actions...:
Opens a dialog to set up file types for print and open actions.
Use ``"%s"`` as a placeholder for the document name.
* Provided file types:
- ODT file: Open Office Writer Document
- PDF file: Adobes(TM) Portable Document Format
- PNG file: Portable Network Graphics format
- TXT file: Pure text file
* Provided actions
- Open: Setting up program system call which opens the specific file type.
- Print: Setting up program system call printing the specific file type.
.. _Menu-Options-Email:
Email...:
+5 -5
View File
@@ -1,9 +1,7 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import time
import datetime
import tryton.rpc as rpc
from tryton.common import message, error, selection, file_open, mailto
from tryton.common import message, selection, file_open, mailto
from tryton.gui.window import Window
from tryton.pyson import PYSONDecoder
from tryton.exceptions import TrytonServerError
@@ -15,6 +13,7 @@ import tryton.common as common
_ = gettext.gettext
class Action(object):
@staticmethod
@@ -71,7 +70,7 @@ class Action(object):
common.process_exception(exception)
return
if not res:
raise Exception, 'ActionNotFound'
raise Exception('ActionNotFound')
action_type = res['type']
try:
res = rpc.execute('model', action_type, 'search_read',
@@ -117,7 +116,8 @@ class Action(object):
domain_context = ctx.copy()
domain_context['context'] = ctx
domain_context['_user'] = rpc._USER
domain = PYSONDecoder(domain_context).decode(action['pyson_domain'])
domain = PYSONDecoder(domain_context).decode(
action['pyson_domain'])
search_context = ctx.copy()
search_context['context'] = ctx
+7 -7
View File
@@ -22,21 +22,18 @@ import logging
from urlparse import urlparse
import threading
from tryton import version
from tryton import config
import tryton.common as common
from tryton.config import CONFIG, CURRENT_DIR, PREFIX, PIXMAPS_DIR, \
TRYTON_ICON, get_config_dir
from tryton.config import CONFIG, get_config_dir
from tryton import translate
from tryton import gui
from tryton.ipc import Client as IPCClient
import traceback
import time
import signal
if not hasattr(gtk.gdk, 'lock'):
class _Lock(object):
__enter__ = gtk.gdk.threads_enter
def __exit__(*ignored):
gtk.gdk.threads_leave()
@@ -44,11 +41,13 @@ if not hasattr(gtk.gdk, 'lock'):
if sys.platform == 'win32':
class Dialog(gtk.Dialog):
def run(self):
with gtk.gdk.lock:
return super(Dialog, self).run()
gtk.Dialog = Dialog
class TrytonClient(object):
"Tryton client"
@@ -103,7 +102,8 @@ class TrytonClient(object):
signal.signal(signal.SIGINT, lambda signum, frame: main.sig_quit())
signal.signal(signal.SIGTERM, lambda signum, frame: main.sig_quit())
if hasattr(signal, 'SIGQUIT'):
signal.signal(signal.SIGQUIT, lambda signum, frame: main.sig_quit())
signal.signal(signal.SIGQUIT,
lambda signum, frame: main.sig_quit())
def excepthook(exctyp, exception, tb):
import common
@@ -135,7 +135,7 @@ class TrytonClient(object):
if sys.platform == 'win32':
while not self.quit_client.isSet():
with gtk.gdk.lock:
running = gtk.main_iteration(True)
gtk.main_iteration(True)
else:
gtk.main()
except KeyboardInterrupt:
+2 -1
View File
@@ -27,7 +27,8 @@ class Button(gtk.Button):
def state_set(self, record):
if record:
states = record.expr_eval(self.attrs.get('states', {}), check_load=False)
states = record.expr_eval(self.attrs.get('states', {}),
check_load=False)
else:
states = {}
if states.get('invisible', False):
+41 -20
View File
@@ -4,6 +4,9 @@ import gtk
import gobject
import pango
BUTTON_BORDER = 10
BUTTON_SPACING = 2
class CellRendererBinary(gtk.GenericCellRenderer):
__gproperties__ = {
@@ -48,7 +51,8 @@ class CellRendererBinary(gtk.GenericCellRenderer):
img_insensitive = img_sensitive.copy()
img_sensitive.saturate_and_pixelate(img_insensitive, 0, False)
width = img_sensitive.get_width()
self.images[key] = (img_sensitive, img_insensitive, width)
height = img_sensitive.get_height()
self.images[key] = (img_sensitive, img_insensitive, width, height)
def do_set_property(self, pspec, value):
setattr(self, pspec.name, value)
@@ -56,24 +60,31 @@ class CellRendererBinary(gtk.GenericCellRenderer):
def do_get_property(self, pspec):
return getattr(self, pspec.name)
def button_width(self, cell_area):
return ((cell_area.width / 2 - (len(self.buttons) + 1) * 2)
/ len(self.buttons))
def button_width(self):
return (sum(width for _, _, width, _ in self.images.itervalues())
+ (2 * (BUTTON_BORDER + BUTTON_SPACING) * len(self.buttons))
- 2 * BUTTON_SPACING)
def on_get_size(self, widget, cell_area=None):
if cell_area is None:
return (0, 0, 30, 18)
else:
return (cell_area.x, cell_area.y, cell_area.width, cell_area.height)
return (cell_area.x, cell_area.y,
cell_area.width, cell_area.height)
def on_start_editing(self, event, widget, path, background_area,
cell_area, flags):
button_width = self.button_width(cell_area)
if event is None:
return
button_width = self.button_width()
for index, button_name in enumerate(self.buttons):
x_offset = button_width * index + 2 * (index + 1)
x_button = cell_area.x + cell_area.width / 2 + 5 + x_offset
if x_button < event.x < x_button + button_width:
_, _, pxbf_width, _ = self.images[button_name]
x_offset = (cell_area.width - button_width
+ (pxbf_width + (2 * BUTTON_BORDER) + BUTTON_SPACING) * index)
x_button = cell_area.x + x_offset
if x_button < event.x < (x_button + pxbf_width
+ (2 * BUTTON_BORDER)):
break
else:
button_name = None
@@ -83,9 +94,10 @@ class CellRendererBinary(gtk.GenericCellRenderer):
return
if not self.size and button_name == 'save':
return
if event is None or event.type == gtk.gdk.BUTTON_PRESS:
if event.type == gtk.gdk.BUTTON_PRESS:
self.clicking = button_name
self.emit(button_name, path)
def timeout(self, widget):
self.clicking = ''
widget.queue_draw()
@@ -99,24 +111,31 @@ class CellRendererBinary(gtk.GenericCellRenderer):
if type(window) == gtk.gdk.Pixmap:
return
button_width = self.button_width()
# display size
layout = widget.create_pango_layout(self.size)
layout.set_font_description(widget.style.font_desc)
w, h = layout.get_size()
x = int(cell_area.x + (cell_area.width - w / pango.SCALE) * 0)
x = int(cell_area.x + cell_area.width - button_width - w / pango.SCALE
- BUTTON_SPACING)
y = int(cell_area.y + (cell_area.height - h / pango.SCALE) / 2)
layout.set_width(((cell_area.width / 2) - 2) * pango.SCALE)
state = gtk.STATE_NORMAL
if flags & gtk.CELL_RENDERER_SELECTED:
state = gtk.STATE_ACTIVE
window.draw_layout(widget.style.text_gc[state], x, y, layout)
if x >= cell_area.x:
widget.style.paint_layout(window, state, True, expose_area,
widget, "cellrendererbinary", x, y, layout)
# display buttons
button_width = self.button_width(cell_area)
for index, button_name in enumerate(self.buttons):
state = gtk.STATE_NORMAL
shadow = gtk.SHADOW_OUT
pxbf_sens, pxbf_insens, pxbf_width = self.images[button_name]
if self.clicking and flags & gtk.CELL_RENDERER_SELECTED:
pxbf_sens, pxbf_insens, pxbf_width, pxbf_height = \
self.images[button_name]
if (self.clicking == button_name
and flags & gtk.CELL_RENDERER_SELECTED):
state = gtk.STATE_ACTIVE
shadow = gtk.SHADOW_IN
if (not self.editable and button_name in ('new', 'clear')
@@ -125,14 +144,16 @@ class CellRendererBinary(gtk.GenericCellRenderer):
pixbuf = pxbf_insens
else:
pixbuf = pxbf_sens
x_offset = (cell_area.width / 2 + 5
+ button_width * index + 2 * (index + 1)) - 1
x_offset = (cell_area.width - button_width
+ (pxbf_width + (2 * BUTTON_BORDER) + BUTTON_SPACING) * index)
if x_offset < 0:
continue
widget.style.paint_box(window, state, shadow,
None, widget, "button", cell_area.x + x_offset, cell_area.y,
button_width, cell_area.height)
pxbf_width + (2 * BUTTON_BORDER), cell_area.height)
window.draw_pixbuf(widget.style.black_gc,
pixbuf, 0, 0,
cell_area.x + x_offset + button_width / 2 - pxbf_width / 2,
cell_area.y)
cell_area.x + x_offset + BUTTON_BORDER,
cell_area.y + (cell_area.height - pxbf_height) / 2)
gobject.type_register(CellRendererBinary)
+14 -3
View File
@@ -54,19 +54,29 @@ class CellRendererButton(gtk.GenericCellRenderer):
x = cell_area.x
y = int(cell_area.y + (cell_area.height - h / pango.SCALE) / 2)
window.draw_layout(widget.style.text_gc[0], x, y, layout)
widget.style.paint_layout(window, state, True, expose_area, widget,
"cellrendererbutton", x, y, layout)
layout = widget.create_pango_layout(self.text)
layout.set_font_description(widget.style.font_desc)
layout.set_ellipsize(pango.ELLIPSIZE_END)
w, h = layout.get_size()
x = int(cell_area.x + (cell_area.width - w / pango.SCALE) / 2)
if cell_area.width < w / pango.SCALE:
x = cell_area.x + 2 / pango.SCALE
else:
x = int(cell_area.x + (cell_area.width - w / pango.SCALE) / 2)
y = int(cell_area.y + (cell_area.height - h / pango.SCALE) / 2)
window.draw_layout(widget.style.text_gc[0], x, y, layout)
layout.set_width((cell_area.width - 4) * pango.SCALE)
layout.set_wrap(pango.WRAP_CHAR)
widget.style.paint_layout(window, state, True, expose_area, widget,
"cellrendererbutton", x, y, layout)
def on_get_size(self, widget, cell_area=None):
if cell_area is None:
return (0, 0, 30, 18)
else:
return (cell_area.x, cell_area.y, cell_area.width, cell_area.height)
return (cell_area.x, cell_area.y,
cell_area.width, cell_area.height)
def on_start_editing(self, event, widget, path, background_area,
cell_area, flags):
@@ -80,6 +90,7 @@ class CellRendererButton(gtk.GenericCellRenderer):
while gtk.events_pending():
gtk.main_iteration()
self.emit("clicked", path)
def timeout(self, widget):
self.clicking = False
widget.queue_draw()
+6 -3
View File
@@ -106,10 +106,13 @@ class CellRendererCombo(gtk.GenericCellRenderer):
editable.modify_text(gtk.STATE_INSENSITIVE, fg_color)
else:
editable.modify_bg(gtk.STATE_ACTIVE, style.bg[gtk.STATE_ACTIVE])
editable.modify_base(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
editable.modify_base(gtk.STATE_NORMAL,
style.base[gtk.STATE_NORMAL])
editable.modify_fg(gtk.STATE_NORMAL, style.fg[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_NORMAL, style.text[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_INSENSITIVE, style.text[gtk.STATE_INSENSITIVE])
editable.modify_text(gtk.STATE_NORMAL,
style.text[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_INSENSITIVE,
style.text[gtk.STATE_INSENSITIVE])
return editable
gobject.type_register(CellRendererCombo)
+10 -6
View File
@@ -3,7 +3,7 @@
import gtk
import gobject
import pango
from date_widget import mapping, DateEntry, compute_date
from date_widget import DateEntry, compute_date
class CellRendererDate(gtk.GenericCellRenderer):
@@ -100,10 +100,13 @@ class CellRendererDate(gtk.GenericCellRenderer):
editable.modify_text(gtk.STATE_INSENSITIVE, fg_color)
else:
editable.modify_bg(gtk.STATE_ACTIVE, style.bg[gtk.STATE_ACTIVE])
editable.modify_base(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
editable.modify_base(gtk.STATE_NORMAL,
style.base[gtk.STATE_NORMAL])
editable.modify_fg(gtk.STATE_NORMAL, style.fg[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_NORMAL, style.text[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_INSENSITIVE, style.text[gtk.STATE_INSENSITIVE])
editable.modify_text(gtk.STATE_NORMAL,
style.text[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_INSENSITIVE,
style.text[gtk.STATE_INSENSITIVE])
if self.text:
editable.set_text(self.text)
@@ -126,9 +129,10 @@ class CellRendererDate(gtk.GenericCellRenderer):
if ok:
self._date_cb(event)
else:
if hasattr(event, 'keyval') and not event.keyval == gtk.keysyms.Escape:
if (hasattr(event, 'keyval')
and not event.keyval == gtk.keysyms.Escape):
dt = widget.date_get()
res= compute_date(self.cmd, dt, widget.format)
res = compute_date(self.cmd, dt, widget.format)
if res:
widget.date_set(res)
self.cmd = ''
-1
View File
@@ -7,7 +7,6 @@ import locale
class CellRendererInteger(CellRendererText):
def on_start_editing(self, event, widget, path, background_area,
cell_area, flags):
editable = super(CellRendererInteger, self).on_start_editing(event,
+6 -3
View File
@@ -100,10 +100,13 @@ class CellRendererText(gtk.GenericCellRenderer):
editable.modify_text(gtk.STATE_INSENSITIVE, fg_color)
else:
editable.modify_bg(gtk.STATE_ACTIVE, style.bg[gtk.STATE_ACTIVE])
editable.modify_base(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
editable.modify_base(gtk.STATE_NORMAL,
style.base[gtk.STATE_NORMAL])
editable.modify_fg(gtk.STATE_NORMAL, style.fg[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_NORMAL, style.text[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_INSENSITIVE, style.text[gtk.STATE_INSENSITIVE])
editable.modify_text(gtk.STATE_NORMAL,
style.text[gtk.STATE_NORMAL])
editable.modify_text(gtk.STATE_INSENSITIVE,
style.text[gtk.STATE_INSENSITIVE])
editable.set_text(self.text)
editable.grab_focus()
-1
View File
@@ -2,7 +2,6 @@
#this repository contains the full copyright notices and license terms.
import gtk
import gobject
import pango
class CellRendererToggle(gtk.GenericCellRenderer):
+120 -119
View File
@@ -11,7 +11,7 @@ import subprocess
import re
import logging
from tryton.config import CONFIG
from tryton.config import TRYTON_ICON, PIXMAPS_DIR, DATA_DIR
from tryton.config import TRYTON_ICON, PIXMAPS_DIR
import time
import sys
import xmlrpclib
@@ -37,7 +37,7 @@ try:
except ImportError:
ssl = None
import dis
from threading import Lock, Semaphore
from threading import Lock
try:
import pytz
except ImportError:
@@ -128,6 +128,7 @@ class TrytonIconFactory(gtk.IconFactory):
ICONFACTORY = TrytonIconFactory()
ICONFACTORY.add_default()
def find_in_path(name):
if os.name == "nt":
sep = ';'
@@ -141,12 +142,14 @@ def find_in_path(name):
return val
return name
def test_server_version(host, port):
version = rpc.server_version(host, port)
if not version:
return False
return version.split('.')[:2] == VERSION.split('.')[:2]
def refresh_dblist(host, port):
'''
Return the number of database available
@@ -158,6 +161,7 @@ def refresh_dblist(host, port):
return -1
return rpc.db_list(host, port)
def refresh_langlist(lang_widget, host, port):
liststore = lang_widget.get_model()
liststore.clear()
@@ -174,17 +178,18 @@ def refresh_langlist(lang_widget, host, port):
liststore.insert(i, (val, key))
if key == lang:
index = i
if key == 'en_US' and index < 0 :
if key == 'en_US' and index < 0:
index = i
i += 1
lang_widget.set_active(index)
return lang_list
def request_server(server_widget):
result = False
parent = get_toplevel_window()
dialog = gtk.Dialog(
title= _('Tryton Connection'),
title=_('Tryton Connection'),
parent=parent,
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |
gtk.WIN_POS_CENTER_ON_PARENT |
@@ -211,7 +216,7 @@ def request_server(server_widget):
entry_server.set_text("localhost")
entry_server.set_activates_default(True)
entry_server.set_width_chars(16)
table.attach(entry_server, 1, 2, 0, 1,yoptions=False,
table.attach(entry_server, 1, 2, 0, 1, yoptions=False,
xoptions=gtk.FILL | gtk.EXPAND)
label_port = gtk.Label(_("Port:"))
label_port.set_alignment(1, 0.5)
@@ -242,6 +247,7 @@ def request_server(server_widget):
dialog.destroy()
return result
def get_toplevel_window():
windows = [x for x in gtk.window_list_toplevels()
if x.window and x.props.visible]
@@ -250,6 +256,7 @@ def get_toplevel_window():
return window
return trans2windows[None]
def get_sensible_widget(window):
from tryton.gui.main import Main
main = Main.get_main()
@@ -260,6 +267,7 @@ def get_sensible_widget(window):
return page.widget
return window
def center_window(window, parent, sensible):
parent_x, parent_y = parent.window.get_origin()
window_allocation = window.get_allocation()
@@ -270,10 +278,11 @@ def center_window(window, parent, sensible):
int((sensible_allocation.height - window_allocation.height) / 2))
window.move(x, y)
def selection(title, values, alwaysask=False):
if not values or len(values)==0:
if not values or len(values) == 0:
return None
elif len(values)==1 and (not alwaysask):
elif len(values) == 1 and (not alwaysask):
key = values.keys()[0]
return (key, values[key])
@@ -331,13 +340,14 @@ def selection(title, values, alwaysask=False):
dialog.destroy()
return res
def file_selection(title, filename='',
action=gtk.FILE_CHOOSER_ACTION_OPEN, preview=True, multi=False,
filters=None):
parent = get_toplevel_window()
if action == gtk.FILE_CHOOSER_ACTION_OPEN:
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN,gtk.RESPONSE_OK)
gtk.STOCK_OPEN, gtk.RESPONSE_OK)
else:
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK)
@@ -403,48 +413,9 @@ def file_selection(title, filename='',
win.destroy()
return filenames
def file_open(filename, type, print_p=False):
if os.name == 'nt':
operation = 'open'
if print_p:
operation = 'print'
try:
os.startfile(os.path.normpath(filename), operation)
except WindowsError:
save_name = file_selection(_('Save As...'),
action=gtk.FILE_CHOOSER_ACTION_SAVE)
if save_name:
file_p = open(filename, 'rb')
save_p = open(save_name, 'wb+')
save_p.write(file_p.read())
save_p.close()
file_p.close()
return
elif sys.platform == 'darwin':
pid = os.fork()
if not pid:
pid = os.fork()
if not pid:
try:
os.execv('/usr/bin/open', ['/usr/bin/open', filename])
except OSError:
sys.exit(0)
time.sleep(0.1)
sys.exit(0)
os.waitpid(pid, 0)
return
cmd = ''
if isinstance(CONFIG['client.actions'], basestring):
CONFIG['client.actions'] = safe_eval(CONFIG['client.actions'])
if type in CONFIG['client.actions']:
if print_p:
cmd = CONFIG['client.actions'][type][1]
else:
cmd = CONFIG['client.actions'][type][0]
if not cmd:
#TODO add dialog box
pass
if not cmd:
def save():
save_name = file_selection(_('Save As...'),
action=gtk.FILE_CHOOSER_ACTION_SAVE)
if save_name:
@@ -453,25 +424,26 @@ def file_open(filename, type, print_p=False):
save_p.write(file_p.read())
save_p.close()
file_p.close()
return
cmd = cmd % filename
args = shlex.split(str(cmd))
prog = find_in_path(args[0])
args[0] = os.path.basename(args[0])
if print_p:
os.spawnv(os.P_WAIT, prog, args)
return
pid = os.fork()
if not pid:
pid = os.fork()
if not pid:
try:
os.execv(prog, args)
except OSError:
sys.exit(0)
time.sleep(0.1)
sys.exit(0)
os.waitpid(pid, 0)
if os.name == 'nt':
operation = 'open'
if print_p:
operation = 'print'
try:
os.startfile(os.path.normpath(filename), operation)
except WindowsError:
save()
elif sys.platform == 'darwin':
try:
subprocess.Popen(['/usr/bin/open', filename])
except OSError:
save()
else:
try:
subprocess.Popen(['xdg-open', filename])
except OSError:
save()
def mailto(to=None, cc=None, subject=None, body=None, attachment=None):
if CONFIG['client.email']:
@@ -485,6 +457,23 @@ def mailto(to=None, cc=None, subject=None, body=None, attachment=None):
args = shlex.split(str(cmd))
subprocess.Popen(args)
return
if os.name != 'nt' and sys.platform != 'darwin':
args = ['xdg-email', '--utf8']
if cc:
args.extend(['--cc', cc])
if subject:
args.extend(['--subject', subject])
if body:
args.extend(['--body', body])
if attachment:
args.extend(['--attach', attachment])
if to:
args.append(to)
try:
subprocess.Popen(args)
return
except OSError:
pass
#http://www.faqs.org/rfcs/rfc2368.html
url = "mailto:"
if to:
@@ -540,7 +529,7 @@ class MessageDialog(UniqueDialog):
def build_dialog(self, parent, message, msg_type):
dialog = gtk.MessageDialog(parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, msg_type,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, msg_type,
gtk.BUTTONS_OK, message)
return dialog
@@ -559,7 +548,8 @@ class WarningDialog(UniqueDialog):
dialog.set_markup('<b>%s</b>' % (to_xml(title)))
dialog.format_secondary_markup(to_xml(message))
else:
dialog.set_markup('<b>%s</b>\n%s' % (to_xml(title), to_xml(message)))
dialog.set_markup(
'<b>%s</b>\n%s' % (to_xml(title), to_xml(message)))
return dialog
warning = WarningDialog()
@@ -659,7 +649,7 @@ class AskDialog(UniqueDialog):
def build_dialog(self, parent, question, visibility):
win = gtk.Dialog('Tryton', parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
win.set_has_separator(True)
@@ -704,9 +694,9 @@ ask = AskDialog()
class ConcurrencyDialog(UniqueDialog):
def build_dialog(self, parent, resource, obj_id, context):
dialog = gtk.Dialog(_('Concurrency Exception'), parent, gtk.DIALOG_MODAL
| gtk.DIALOG_DESTROY_WITH_PARENT | gtk.WIN_POS_CENTER_ON_PARENT
| gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
dialog = gtk.Dialog(_('Concurrency Exception'), parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |
gtk.WIN_POS_CENTER_ON_PARENT | gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
dialog.set_has_separator(True)
dialog.set_default_response(gtk.RESPONSE_CANCEL)
hbox = gtk.HBox()
@@ -830,15 +820,16 @@ class ErrorDialog(UniqueDialog):
if title == details:
title = ''
log = logging.getLogger('common.message')
log.error(details)
log.error(details + '\n' + title)
response = super(ErrorDialog, self).__call__(title, details)
if response == gtk.RESPONSE_OK:
send_bugtracker(details)
send_bugtracker(title, details)
error = ErrorDialog()
def send_bugtracker(msg):
def send_bugtracker(title, msg):
from tryton import rpc
parent = get_toplevel_window()
win = gtk.Dialog(_('Bug Tracker'), parent,
@@ -897,21 +888,15 @@ def send_bugtracker(msg):
protocol = 'http'
if ssl or hasattr(socket, 'ssl'):
protocol = 'https'
server = xmlrpclib.Server(('%s://%s:%s@' + CONFIG['roundup.xmlrpc'])
% (protocol, user, password), allow_none=True)
server = xmlrpclib.Server(
('%s://%s:%s@' + CONFIG['roundup.xmlrpc'])
% (protocol, user, password), allow_none=True)
if hashlib:
msg_md5 = hashlib.md5(msg).hexdigest()
msg_md5 = hashlib.md5(msg + '\n' + title).hexdigest()
else:
msg_md5 = md5.new(msg).hexdigest()
# use the last line of the message as title
title = '[no title]'
for line in msg.splitlines():
#don't use empty line nor ^ from sql error
if line and '^' != line.strip():
if len(line) > 128:
title = line[:128] + '...'
else:
title = line
msg_md5 = md5.new(msg + '\n' + title).hexdigest()
if not title:
title = '[no title]'
issue_id = None
msg_ids = server.filter('msg', None, {'summary': str(msg_md5)})
if msg_ids:
@@ -921,9 +906,10 @@ def send_bugtracker(msg):
if issue_id:
# issue to same message already exists, add user to nosy-list
server.set('issue' + str(issue_id), *['nosy=+' + user])
message(_('The same bug was already reported by another user.\n' \
'To keep you informed your username is added to the nosy-list of this issue') + \
'%s' % issue_id)
message(
_('The same bug was already reported by another user.\n'
'To keep you informed your username is added '
'to the nosy-list of this issue') + '%s' % issue_id)
else:
# create a new issue for this error-message
# first create message
@@ -934,23 +920,27 @@ def send_bugtracker(msg):
'nosy=' + user, 'title=' + title, 'priority=bug'])
message(_('Created new bug with ID ') + \
'issue%s' % issue_id)
webbrowser.open(CONFIG['roundup.url'] + 'issue%s' % issue_id, new=2)
webbrowser.open(CONFIG['roundup.url'] + 'issue%s' % issue_id,
new=2)
except (socket.error, xmlrpclib.Fault), exception:
if (isinstance(exception, xmlrpclib.Fault)
and 'roundup.cgi.exceptions.Unauthorised' in
exception.faultString):
message(_('Connection error!\nBad username or password!'))
return send_bugtracker(msg, parent)
return send_bugtracker(title, msg)
tb_s = reduce(lambda x, y: x + y,
traceback.format_exception(sys.exc_type,
sys.exc_value, sys.exc_traceback))
message(_('Exception:') + '\n' + tb_s, msg_type=gtk.MESSAGE_ERROR)
def to_xml(string):
return string.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')
return string.replace('&', '&amp;'
).replace('<', '&lt;').replace('>', '&gt;')
PLOCK = Lock()
def process_exception(exception, *args, **kwargs):
if isinstance(exception, TrytonError):
@@ -1049,6 +1039,7 @@ def process_exception(exception, *args, **kwargs):
error(error_title, error_detail)
return False
def node_attributes(node):
result = {}
attrs = node.attributes
@@ -1058,6 +1049,7 @@ def node_attributes(node):
result[str(attrs.item(i).localName)] = str(attrs.item(i).nodeValue)
return result
def hex2rgb(hexstring, digits=2):
"""
Converts a hexstring color to a rgb tuple.
@@ -1068,11 +1060,12 @@ def hex2rgb(hexstring, digits=2):
if isinstance(hexstring, (tuple, list)):
return hexstring
top = float(int(digits * 'f', 16))
r = int(hexstring[1:digits+1], 16)
g = int(hexstring[digits+1:digits*2+1], 16)
b = int(hexstring[digits*2+1:digits*3+1], 16)
r = int(hexstring[1:digits + 1], 16)
g = int(hexstring[digits + 1:digits * 2 + 1], 16)
b = int(hexstring[digits * 2 + 1:digits * 3 + 1], 16)
return r / top, g / top, b / top
def clamp(minValue, maxValue, value):
"""Make sure value is between minValue and maxValue"""
if value < minValue:
@@ -1081,12 +1074,14 @@ def clamp(minValue, maxValue, value):
return maxValue
return value
def lighten(r, g, b, amount):
"""Return a lighter version of the color (r, g, b)"""
return (clamp(0.0, 1.0, r + amount),
clamp(0.0, 1.0, g + amount),
clamp(0.0, 1.0, b + amount))
def generateColorscheme(masterColor, keys, light=0.06):
"""
Generates a dictionary where the keys match the keys argument and
@@ -1121,7 +1116,7 @@ class DBProgress(object):
def update(self, combo, progressbar, callback, dbname=''):
self.db_info = None
thread = threading.Thread(target=self.start).start()
threading.Thread(target=self.start).start()
gobject.timeout_add(100, self.end, combo, progressbar, callback,
dbname)
@@ -1206,7 +1201,8 @@ class RPCProgress(object):
hbox.pack_start(img, expand=True, fill=False)
vbox2 = gtk.VBox(False, 0)
label = gtk.Label()
label.set_markup('<b>'+_('Operation in progress')+'</b>')
label.set_markup(
'<b>' + _('Operation in progress') + '</b>')
label.set_alignment(0.0, 0.5)
vbox2.pack_start(label, expand=True, fill=False)
vbox2.pack_start(gtk.HSeparator(), expand=True, fill=True)
@@ -1272,8 +1268,8 @@ COLOR_SCHEMES = {
}
COLORS = {
'invalid':'#ff6969',
'required':'#d2d2ff',
'invalid': '#ff6969',
'required': '#d2d2ff',
}
DT_FORMAT = '%Y-%m-%d'
@@ -1286,7 +1282,7 @@ FLOAT_TIME_CONV = {
'w': 168,
'd': 24,
'h': 1,
'm': 1.0/60,
'm': 1.0 / 60,
}
FLOAT_TIME_SEPS = {
@@ -1298,6 +1294,7 @@ FLOAT_TIME_SEPS = {
'm': _('m'),
}
def text_to_float_time(text, conv=None):
try:
try:
@@ -1340,6 +1337,7 @@ def text_to_float_time(text, conv=None):
except ValueError:
return 0.0
def float_time_to_text(val, conv=None):
if conv:
tmp_conv = FLOAT_TIME_CONV.copy()
@@ -1362,7 +1360,7 @@ def float_time_to_text(val, conv=None):
val = val - days * conv['d']
hours = int(val)
val = val - hours
mins = int((val% 1 + 0.01) / conv['m'])
mins = int((val % 1 + 0.01) / conv['m'])
if years:
value += ' ' + locale.format('%d', years, True) + FLOAT_TIME_SEPS['Y']
if months:
@@ -1376,6 +1374,7 @@ def float_time_to_text(val, conv=None):
value = value.strip()
return value
def filter_domain(domain):
'''
Return the biggest subset of domain with only AND operator
@@ -1394,23 +1393,23 @@ def filter_domain(domain):
return res
_ALLOWED_CODES = set(dis.opmap[x] for x in [
'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP',
'BUILD_LIST','BUILD_MAP','BUILD_TUPLE',
'LOAD_CONST','RETURN_VALUE','STORE_SUBSCR',
'UNARY_POSITIVE','UNARY_NEGATIVE','UNARY_NOT',
'UNARY_INVERT','BINARY_POWER','BINARY_MULTIPLY',
'BINARY_DIVIDE','BINARY_FLOOR_DIVIDE','BINARY_TRUE_DIVIDE',
'BINARY_MODULO','BINARY_ADD','BINARY_SUBTRACT',
'BINARY_LSHIFT','BINARY_RSHIFT','BINARY_AND','BINARY_XOR', 'BINARY_OR',
'STORE_MAP', 'LOAD_NAME', 'CALL_FUNCTION', 'COMPARE_OP', 'LOAD_ATTR',
'STORE_NAME', 'GET_ITER', 'FOR_ITER', 'LIST_APPEND', 'JUMP_ABSOLUTE',
'DELETE_NAME', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'JUMP_IF_FALSE_OR_POP',
'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE', 'POP_JUMP_IF_TRUE',
'BINARY_SUBSCR',
] if x in dis.opmap)
'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP', 'BUILD_LIST',
'BUILD_MAP', 'BUILD_TUPLE', 'LOAD_CONST', 'RETURN_VALUE',
'STORE_SUBSCR', 'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT',
'UNARY_INVERT', 'BINARY_POWER', 'BINARY_MULTIPLY', 'BINARY_DIVIDE',
'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE', 'BINARY_MODULO',
'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_LSHIFT', 'BINARY_RSHIFT',
'BINARY_AND', 'BINARY_XOR', 'BINARY_OR', 'STORE_MAP', 'LOAD_NAME',
'CALL_FUNCTION', 'COMPARE_OP', 'LOAD_ATTR', 'STORE_NAME', 'GET_ITER',
'FOR_ITER', 'LIST_APPEND', 'JUMP_ABSOLUTE', 'DELETE_NAME',
'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'JUMP_IF_FALSE_OR_POP',
'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE', 'POP_JUMP_IF_TRUE',
'BINARY_SUBSCR',
] if x in dis.opmap)
_SAFE_EVAL_CACHE = {}
def safe_eval(source, data=None):
if '__subclasses__' in source:
raise ValueError('__subclasses__ not allowed')
@@ -1447,6 +1446,7 @@ def safe_eval(source, data=None):
'dict': dict,
}}, data)
def timezoned_date(date):
if pytz and rpc.CONTEXT.get('timezone'):
lzone = pytz.timezone(rpc.CONTEXT['timezone'])
@@ -1456,6 +1456,7 @@ def timezoned_date(date):
date = ldt
return date
def humanize(size):
for x in ('bytes', 'KB', 'MB', 'GB', 'TB', 'PB'):
if size < 1000:
+15 -8
View File
@@ -24,6 +24,7 @@ mapping = {
'%p': ('__', '([_ AP][_ M])'),
}
class DateEntry(gtk.Entry):
def __init__(self, format, callback=None, callback_process=None):
@@ -68,7 +69,8 @@ class DateEntry(gtk.Entry):
return
if self.mode_cmd:
if self.callback: self.callback(value)
if self.callback:
self.callback(value)
self.stop_emission('insert-text')
return
@@ -110,7 +112,8 @@ class DateEntry(gtk.Entry):
end = len(self.initial_value)
if end < 0:
end = 0
while (start > 0) and (self.initial_value[start] not in ['_', ' ','0','X']):
while (start > 0
and self.initial_value[start] not in ['_', ' ', '0', 'X']):
start -= 1
text = self.get_text()
text = text[:start] + self.initial_value[start:end] + text[end:]
@@ -179,15 +182,16 @@ class DateEntry(gtk.Entry):
for a in range(len(self.initial_value)):
if self.initial_value[a] == text[a]:
text = text[:a] + default[a] + text[a+1:]
text = text[:a] + default[a] + text[a + 1:]
return text
def test_date(self, text):
default = datetime_strftime(datetime.datetime(2000, 1, 1),
self.format)
try:
time.strptime(self.compute_date(text, default=default, year='2000'),
self.format)
time.strptime(
self.compute_date(text, default=default, year='2000'),
self.format)
except ValueError:
return False
return True
@@ -287,7 +291,8 @@ class ComplexEntry(gtk.HBox):
self.widget_cmd.show()
self._date_cb(event)
else:
if hasattr(event, 'keyval') and not event.keyval == gtk.keysyms.Escape:
if (hasattr(event, 'keyval')
and not event.keyval == gtk.keysyms.Escape):
cmd = self.widget_cmd.get_text()
dt = self.widget.date_get() or datetime.datetime.now()
res = compute_date(cmd, dt, self.widget.format)
@@ -296,6 +301,7 @@ class ComplexEntry(gtk.HBox):
self.widget_cmd.set_text('')
self.widget_cmd.hide()
def compute_date(cmd, dt, format):
lst = {
'^=(\d+)d$': lambda dt, r: dt + \
@@ -320,8 +326,8 @@ def compute_date(cmd, dt, format):
relativedelta(months=int(r.group(1))),
'^([\\+-]\d+)y$': lambda dt, r: dt + \
relativedelta(years=int(r.group(1))),
'^=$': lambda dt,r: datetime.datetime.now(),
'^-$': lambda dt,r: False
'^=$': lambda dt, r: datetime.datetime.now(),
'^-$': lambda dt, r: False
}
for r, f in lst.items():
groups = re.match(r, cmd)
@@ -336,6 +342,7 @@ def compute_date(cmd, dt, format):
if __name__ == '__main__':
win = gtk.Window()
win.set_title('gtk.Entry subclass')
def cb(window, event):
gtk.main_quit()
win.connect('delete-event', cb)
+2 -1
View File
@@ -3,6 +3,7 @@
#Copyright (c) 2002-2007 John D. Hunter; All Rights Reserved
import time
def datetime_strftime(dt, fmt):
'''
Allow datetime strftime formatting for years before 1900.
@@ -46,5 +47,5 @@ def datetime_strftime(dt, fmt):
s = s1
syear = "%04d" % (dt.year,)
for site in sites:
s = s[:site] + syear + s[site+4:]
s = s[:site] + syear + s[site + 4:]
return s
+31 -4
View File
@@ -4,6 +4,7 @@
import operator
import types
def in_(a, b):
if isinstance(a, (list, tuple)):
return any(operator.contains(b, x) for x in a)
@@ -28,6 +29,7 @@ OPERATORS = {
'not child_of': lambda a, b: True,
}
def locale_part(expression, field_name):
if expression == field_name:
return 'id'
@@ -36,12 +38,14 @@ def locale_part(expression, field_name):
return local
return expression
def is_leaf(expression):
return (isinstance(expression, (list, tuple))
and len(expression) > 2
and isinstance(expression[1], basestring)
and expression[1] in OPERATORS)
def eval_leaf(part, context, boolop=operator.and_):
field, operand, value = part[:3]
if '.' in field:
@@ -54,6 +58,7 @@ def eval_leaf(part, context, boolop=operator.and_):
return True
return OPERATORS[operand](context[field], value)
def inverse_leaf(domain):
if domain in ('AND', 'OR'):
return domain
@@ -67,6 +72,7 @@ def inverse_leaf(domain):
else:
return map(inverse_leaf, domain)
def eval_domain(domain, context, boolop=operator.and_):
"compute domain boolean value according to the context"
if is_leaf(domain):
@@ -83,6 +89,7 @@ def eval_domain(domain, context, boolop=operator.and_):
return boolop(eval_domain(domain[0], context),
eval_domain(domain[1:], context, boolop))
def localize_domain(domain, field_name=None):
"returns only locale part of domain. eg: langage.code -> code"
if domain in ('AND', 'OR', True, False):
@@ -97,6 +104,7 @@ def localize_domain(domain, field_name=None):
else:
return [localize_domain(part, field_name) for part in domain]
def unlocalize_domain(domain, fieldname):
if domain in ('AND', 'OR', True, False):
return domain
@@ -105,6 +113,7 @@ def unlocalize_domain(domain, fieldname):
else:
return [unlocalize_domain(part, fieldname) for part in domain]
def simplify(domain):
"remove unused domain delimiter"
if is_leaf(domain):
@@ -120,6 +129,7 @@ def simplify(domain):
else:
return [simplify(branch) for branch in domain]
def merge(domain, domoperator=None):
if not domain or domain in ('AND', 'OR'):
return []
@@ -146,6 +156,7 @@ def parse(domain):
else:
return And(domain[1:] if domain[0] == 'AND' else domain)
def domain_inversion(domain, symbol, context=None):
"""compute an inversion of the domain eventually the context is used to
simplify the expression"""
@@ -169,7 +180,10 @@ class And(object):
self.variables |= expression.variables
def base(self, expression):
return expression if '.' not in expression else expression.split('.')[0]
if '.' not in expression:
return expression
else:
return expression.split('.')[0]
def inverse(self, symbol, context):
result = []
@@ -209,8 +223,8 @@ class Or(And):
if (symbol not in self.variables
and not known_variables >= self.variables):
# In this case we don't know anything about this OR part, we
# consider it to be True (because people will have the constraint on
# this part later).
# consider it to be True (because people will have the constraint
# on this part later).
return True
for part in self.branches:
if isinstance(part, And):
@@ -244,6 +258,7 @@ class Or(And):
else:
return simplify(['OR'] + result)
# Test stuffs
def test_simple_inversion():
domain = [['x', '=', 3]]
@@ -258,6 +273,7 @@ def test_simple_inversion():
domain = [['x.id', '>', 5]]
assert domain_inversion(domain, 'x') == [['x.id', '>', 5]]
def test_and_inversion():
domain = [['x', '=', 3], ['y', '>', 5]]
assert domain_inversion(domain, 'x') == [['x', '=', 3]]
@@ -274,11 +290,13 @@ def test_and_inversion():
assert domain_inversion(domain, 'y', {'x': 3}) == [['y', '<', 3]]
assert domain_inversion(domain, 'x') == [['x.id', '>', 5]]
def test_or_inversion():
domain = ['OR', ['x', '=', 3], ['y', '>', 5], ['z', '=', 'abc']]
assert domain_inversion(domain, 'x') == [['x', '=', 3]]
assert domain_inversion(domain, 'x', {'y': 4}) == [['x', '=', 3]]
assert domain_inversion(domain, 'x', {'y': 4, 'z': 'ab'}) == [['x', '=', 3]]
assert domain_inversion(domain, 'x', {'y': 4, 'z': 'ab'}) ==\
[['x', '=', 3]]
assert domain_inversion(domain, 'x', {'y': 7}) == True
assert domain_inversion(domain, 'x', {'y': 7, 'z': 'b'}) == True
assert domain_inversion(domain, 'x', {'z': 'abc'}) == True
@@ -299,6 +317,7 @@ def test_or_inversion():
assert domain_inversion(domain, 'length', {'length': 0, 'name': 'n'}) ==\
[['length', '>', 5]]
def test_orand_inversion():
domain = ['OR', [['x', '=', 3], ['y', '>', 5], ['z', '=', 'abc']],
[['x', '=', 4]], [['y', '>', 6]]]
@@ -308,6 +327,7 @@ def test_orand_inversion():
assert domain_inversion(domain, 'x', {'y': 7}) == True
assert domain_inversion(domain, 'x', {'z': 'ab'}) == True
def test_andor_inversion():
domain = [['OR', ['x', '=', 4], ['y', '>', 6]], ['z', '=', 3]]
assert domain_inversion(domain, 'z') == [['z', '=', 3]]
@@ -315,6 +335,7 @@ def test_andor_inversion():
assert domain_inversion(domain, 'z', {'x': 5, 'y': 5}) == False
assert domain_inversion(domain, 'z', {'x': 5, 'y': 7}) == [['z', '=', 3]]
def test_andand_inversion():
domain = [[['x', '=', 4], ['y', '>', 6]], ['z', '=', 3]]
assert domain_inversion(domain, 'z') == [['z', '=', 3]]
@@ -325,6 +346,7 @@ def test_andand_inversion():
domain = [[['x', '=', 4], ['y', '>', 6], ['z', '=', 2]], [['w', '=', 2]]]
assert domain_inversion(domain, 'z', {'x': 4}) == [['z', '=', 2]]
def test_oror_inversion():
domain = ['OR', ['OR', ['x', '=', 3], ['y', '>', 5]],
['OR', ['x', '=', 2], ['z', '=', 'abc']],
@@ -342,6 +364,7 @@ def test_oror_inversion():
assert domain_inversion(domain, 'x', {'y': 4, 'z': 'b'}) == \
['OR', [['x', '=', 3]], [['x', '=', 2]]]
def test_parse():
domain = parse([['x', '=', 5]])
assert domain.variables == set('x')
@@ -352,6 +375,7 @@ def test_parse():
domain = parse([[['x', '=', 4], ['y', '>', 6]], ['z', '=', 3]])
assert domain.variables == set('xyz')
def test_simplify():
domain = [['x', '=', 3]]
assert simplify(domain) == [['x', '=', 3]]
@@ -364,6 +388,7 @@ def test_simplify():
domain = ['OR', ['x', '=', 3], ['AND', ['y', '=', 5]]]
assert simplify(domain) == ['OR', ['x', '=', 3], [['y', '=', 5]]]
def test_merge():
domain = [['x', '=', 6], ['y', '=', 7]]
assert merge(domain) == ['AND', ['x', '=', 6], ['y', '=', 7]]
@@ -391,6 +416,7 @@ def test_merge():
['OR', ['c', '=', 3], ['AND', ['d', '=', 4], ['d2', '=', 6]]],
['d', '=', 5], ['e', '=', 6], ['f', '=', 7]]
def test_evaldomain():
domain = [['x', '>', 5]]
assert eval_domain(domain, {'x': 6})
@@ -423,6 +449,7 @@ def test_evaldomain():
assert not eval_domain(domain, {'x': 5})
assert not eval_domain(domain, {'x': 11})
def test_localize():
domain = [['x', '=', 5]]
assert localize_domain(domain) == [['x', '=', 5]]
+2 -2
View File
@@ -20,8 +20,8 @@ class EvalEnvironment(dict):
if self.eval_type == 'eval':
return self.parent.get_eval(check_load=self.check_load)[item]
else:
return self.parent.group.fields[item].get_on_change_value(self.parent,
check_load=self.check_load)
return self.parent.group.fields[item].get_on_change_value(
self.parent, check_load=self.check_load)
def __getattr__(self, item):
try:
+82 -31
View File
@@ -32,6 +32,7 @@ OPERATORS = set((
'ilike',
'not ilike'))
def operator(field):
type_ = field['type'] if field else ''
if type_ in ('char', 'text', 'many2one') or not field:
@@ -68,7 +69,7 @@ def cast(field, value):
return key
return value
elif type_ == 'datetime':
value = value.replace(' : ', ':') # Parser add spaces arround :
value = value.replace(' : ', ':') # Parser add spaces arround :
try:
value = datetime.datetime(*time.strptime(value,
date_format() + ' ' + HM_FORMAT)[:6])
@@ -87,6 +88,7 @@ def cast(field, value):
else:
return value
def quote(value):
for test in (':', ' '):
if test in value:
@@ -127,15 +129,16 @@ class Base(object):
right = property(_get_right, _set_right)
def domain(self, parent_field=None):
raise NotImplementedError("domain method is missing on %s" % \
self.__class__)
raise NotImplementedError("domain method is missing on %s"
% self.__class__)
def complete_fmt(self, left, value, right):
return (self.fmt % (left, value, right)).strip()
def complete(self, parent_field=None):
left = self.left and tuple(self.left.complete()) or ('',)
right = self.right and tuple(self.right.complete(parent_field)) or ('',)
right = (self.right and tuple(self.right.complete(parent_field))
or ('',))
for lvalue in left:
for rvalue in right:
@@ -144,15 +147,15 @@ class Base(object):
def split(self, words=""):
field, value = None, ' '.join(words)
for i in xrange(len(words)):
candidate = ' '.join(words[-i-1:])
candidate = ' '.join(words[-i - 1:])
if candidate.lower() in self.parser.dom_fields:
field = self.parser.dom_fields[candidate.lower()]
value = ' '.join(words[:-i-1])
value = ' '.join(words[:-i - 1])
return field, value
def __str__(self, prefix=''):
res = '%s[%s] %s'% (prefix, self.pos or 'root', self.value)
res = '%s[%s] %s' % (prefix, self.pos or 'root', self.value)
res = res.ljust(30) + str(type(self))
if self.left:
res += "\n" + self.left.__str__(' ' + prefix)
@@ -202,8 +205,8 @@ class Literal(Base):
candidate = word + candidate
for field in suggestions:
if field.lower().startswith(candidate.lower()):
if words[pos+1:]:
new_item = ' '.join(reversed(words[pos+1:]))
if words[pos + 1:]:
new_item = ' '.join(reversed(words[pos + 1:]))
new_item += ' ' + field
else:
new_item = field
@@ -221,7 +224,7 @@ class Literal(Base):
if field.lower().startswith(candidate.lower()):
new_item = quote(field)
if (i-len(words) < 0) and words[i-len(words):]:
if (i - len(words) < 0) and words[i - len(words):]:
find = False
for item in self.suggest(words[i - len(words):],
self.parser.sugg_fields):
@@ -230,7 +233,7 @@ class Literal(Base):
find = True
if find:
break
new_item += ' ' + ' '.join(words[i-len(words):])
new_item += ' ' + ' '.join(words[i - len(words):])
yield new_item
break
@@ -246,7 +249,8 @@ class Literal(Base):
if parent_field:
if parent_field['type'] in ('selection', 'reference', 'boolean'):
if parent_field['type'] in ('selection', 'reference'):
suggestions = tuple(x[1] for x in parent_field['selection'])
suggestions = tuple(x[1]
for x in parent_field['selection'])
else:
suggestions = (_('Y'), _('Yes'), _('True'), '1', _('N'),
_('No'), _('False'), '0')
@@ -369,12 +373,13 @@ class Colon(Base, InfixMixin):
if field['type'] in ('char', 'text', 'many2one'):
value += '%'
domain.append((field['name'] , operator(field), value))
domain.append((field['name'], operator(field), value))
else:
domain.extend(self.right.domain(field))
return domain
class DoubleDot(Base, InfixMixin):
value = '..'
lbp = 70
@@ -436,7 +441,7 @@ class Comma(Base, InfixMixin):
node = node.left
walk(self.left, values)
return [(field_name, 'in' , list(reversed(values)))]
return [(field_name, 'in', list(reversed(values)))]
class Comparator(Base, InfixMixin):
@@ -464,15 +469,19 @@ class Comparator(Base, InfixMixin):
res.append((field_name, self.value, value))
return res
class LessThan(Comparator):
value = '<'
class LessThanOrEqual(Comparator):
value = '<='
class BiggerThan(Comparator):
value = '>'
class BiggerThanOrEqual(Comparator):
value = '>='
@@ -519,6 +528,7 @@ class Not(Base, InfixMixin):
res.append((field_name, oper, value))
return res
class NotEqual(Equal):
value = '!='
fmt = '%s%s%s'
@@ -574,6 +584,7 @@ class CloseParenthesis(Base):
return self.left.domain()
return []
class OpenParenthesis(Base):
value = '('
lbp = 10
@@ -701,7 +712,7 @@ class DomainParser:
return left
def next_token(self):
try :
try:
new_tk = self.token_flow.next()
except StopIteration:
return End(self)
@@ -808,6 +819,7 @@ class DomainParser:
domain = domain[1:]
else:
nary = ' '
def format(expression):
if (isinstance(expression, (list, tuple))
and len(expression) > 2
@@ -892,6 +904,7 @@ def test_parser():
}
return DomainParser(fields)
def test_selection_complete():
parser = test_parser()
assert list(parser.parse('S').complete()) == ['Selection:']
@@ -912,6 +925,7 @@ def test_selection_complete():
assert list(parser.parse('Selection: Ham foo').complete()) == [
'Selection: Ham foo']
def test_selection_domain():
parser = test_parser()
assert parser.parse('S').domain() == [('rec_name', 'ilike', 'S%')]
@@ -923,6 +937,7 @@ def test_selection_domain():
assert parser.parse('Selection: Ham; Spam').domain() == [
('selection', 'in', ['ham', 'spam'])]
def test_selection_string():
parser = test_parser()
assert parser.string([]) == ''
@@ -933,6 +948,7 @@ def test_selection_string():
assert parser.string([('selection', 'in', ['ham', 'spam'])]) == \
'Selection: Ham; Spam'
def test_boolean_complete():
parser = test_parser()
assert list(parser.parse('b').complete()) == ['Boolean:']
@@ -943,13 +959,15 @@ def test_boolean_complete():
assert list(parser.parse('boolean: y').complete()) == ['Boolean: Y',
'Boolean: Yes']
assert list(parser.parse('boolean: 1').complete()) == ['Boolean: 1']
assert list(parser.parse('boolean: false').complete()) == ['Boolean: False']
assert list(parser.parse('boolean: false').complete()) ==\
['Boolean: False']
assert list(parser.parse('boolean: f').complete()) == ['Boolean: False']
assert list(parser.parse('boolean: n').complete()) == ['Boolean: N',
'Boolean: No']
assert list(parser.parse('boolean: 0').complete()) == ['Boolean: 0']
assert list(parser.parse('boolean: =1').complete()) == ['Boolean: =1']
def test_boolean_domain():
parser = test_parser()
assert parser.parse('B').domain() == [('rec_name', 'ilike', 'B%')]
@@ -961,6 +979,7 @@ def test_boolean_domain():
assert parser.parse('Boolean: true; false').domain() == [
('boolean', 'in', [True, False])]
def test_boolean_string():
parser = test_parser()
assert parser.string([('boolean', '=', True)]) == 'Boolean: True'
@@ -968,6 +987,7 @@ def test_boolean_string():
assert parser.string([('boolean', 'in', [True, False])]) == \
'Boolean: True; False'
def test_char_complete():
parser = test_parser()
assert list(parser.parse('c').complete()) == ['Char:']
@@ -977,6 +997,7 @@ def test_char_complete():
'Char: "foo bar"']
assert list(parser.parse('char: =foo').complete()) == ['Char: =foo']
def test_char_domain():
parser = test_parser()
assert parser.parse('c').domain() == [('rec_name', 'ilike', 'c%')]
@@ -989,7 +1010,9 @@ def test_char_domain():
assert parser.parse('Char: !=foo').domain() == [('char', '!=', 'foo')]
assert parser.parse('Char: != foo').domain() == [('char', '!=', 'foo')]
assert parser.parse('Char: 2; 3').domain() == [('char', 'in', ['2', '3'])]
assert parser.parse('Char:! 2; 3').domain() == [('char', 'not in', ['2', '3'])]
assert parser.parse('Char:! 2; 3').domain() == [
('char', 'not in', ['2', '3'])]
def test_char_string():
parser = test_parser()
@@ -1007,6 +1030,7 @@ def test_numeric_complete():
assert list(parser.parse('numeric: 100').complete()) == [
'Numeric: 100']
def test_numeric_domain():
parser = test_parser()
assert parser.parse('Numeric: >100').domain() == [
@@ -1022,17 +1046,20 @@ def test_numeric_domain():
assert parser.parse('Numeric: 2; 3').domain() == [
('numeric', 'in', [Decimal(2), Decimal(3)])]
def test_numeric_string():
parser = test_parser()
assert parser.string([('numeric', '=', Decimal(100))]) == 'Numeric: 100.00'
assert parser.string([('numeric', 'in', [Decimal(2), Decimal(3)])]) == \
'Numeric: 2.00; 3.00'
def test_integer_complete():
parser = test_parser()
assert list(parser.parse('integer: 42').complete()) == ['Integer: 42']
assert list(parser.parse('integer: 2;3').complete()) == ['Integer: 2; 3']
def test_integer_domain():
parser = test_parser()
assert parser.parse('Integer: >42').domain() == [
@@ -1050,6 +1077,7 @@ def test_integer_domain():
assert parser.parse('Integer: 2; 3').domain() == [
('integer', 'in', [2, 3])]
def test_integer_string():
parser = test_parser()
assert parser.string([('integer', '=', 42)]) == 'Integer: 42'
@@ -1061,6 +1089,7 @@ def test_float_complete():
parser = test_parser()
assert list(parser.parse('float: 3.14').complete()) == ['Float: 3.14']
def test_float_domain():
parser = test_parser()
assert parser.parse('Float: >3.14').domain() == [
@@ -1076,6 +1105,7 @@ def test_float_domain():
assert parser.parse('Float: foo').domain() == [
('float', '=', False)]
def test_float_string():
parser = test_parser()
assert parser.string([('float', '=', 3.14)]) == 'Float: 3.14'
@@ -1084,6 +1114,7 @@ def test_float_string():
assert parser.string([('float', 'not in', [3.14, 42])]) == \
'Float: !3.14; 42.00'
def test_date_complete():
today = datetime.date.today()
today_str = datetime_strftime(today, date_format())
@@ -1113,6 +1144,7 @@ def test_date_complete():
'Date: 12/04/2002',
]
def test_date_domain():
today = datetime.date.today()
today_str = datetime_strftime(today, date_format())
@@ -1127,7 +1159,9 @@ def test_date_domain():
assert parser.parse('Date: 12/4/2002').domain() == [
('date', '=', datetime.date(2002, 12, 4))]
assert parser.parse('Date: 12/4/2002; 1/1/1970').domain() == [
('date', 'in', [datetime.date(2002, 12, 4), datetime.date(1970, 1, 1)])]
('date', 'in', [datetime.date(2002, 12, 4),
datetime.date(1970, 1, 1)])]
def test_date_string():
parser = test_parser()
@@ -1138,6 +1172,7 @@ def test_date_string():
datetime.date(1970, 1, 1)])]) == \
'Date: 12/04/2002; 01/01/1970'
def test_datetime_complete():
today = datetime.date.today()
today_str = datetime_strftime(today, date_format())
@@ -1149,6 +1184,7 @@ def test_datetime_complete():
assert list(parser.parse('Date Time: ' + today_str + ' 12:30:00'
).complete()) == ['Date Time: "' + today_str + ' 12:30:00"']
def test_datetime_domain():
today = datetime.date.today()
today_str = datetime_strftime(today, date_format())
@@ -1170,6 +1206,7 @@ def test_datetime_domain():
datetime.datetime.combine(datetime.date(1970, 1, 1),
datetime.time.min)])]
def test_datetime_string():
parser = test_parser()
assert parser.string([('datetime', '=', False)]) == 'Date Time: ='
@@ -1184,6 +1221,7 @@ def test_datetime_string():
datetime.time.min)])]) == \
'Date Time: 12/04/2002; 01/01/1970'
def test_composite_complete():
today = datetime.date.today()
today_str = datetime_strftime(today, date_format())
@@ -1230,6 +1268,7 @@ def test_composite_complete():
'Selection: Ham or Selection: Spamham',
]
def test_composite_domain():
parser = test_parser()
assert parser.parse('char: foo boolean: false').domain() == [
@@ -1250,9 +1289,9 @@ def test_composite_domain():
assert parser.parse('char: foo or char: bar or boolean: 0').domain() == [
'OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%'),
('boolean', '=', False)]
assert parser.parse('(char: foo or char: bar) and boolean: 1').domain() == [
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
('boolean', '=', True)]
assert parser.parse('(char: foo or char: bar) and boolean: 1').domain() ==\
[['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
('boolean', '=', True)]
assert parser.parse('(char: foo or char: bar) boolean: 1').domain() == [
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
('boolean', '=', True)]
@@ -1260,15 +1299,18 @@ def test_composite_domain():
).domain() == [
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
('datetime', '=', datetime.datetime(2002, 12, 4, 0, 0))]
assert parser.parse('(char: foo or char: bar) Date Time: 12/4/2002').domain() == [
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
('datetime', '=', datetime.datetime(2002, 12, 4, 0, 0))]
assert parser.parse('char: foo or (char: bar and boolean: 1)').domain() == [
'OR', ('char', 'ilike', 'foo%'), [
('char', 'ilike', 'bar%'), ('boolean', '=', True)]]
assert parser.parse('char: foo and (char: bar or boolean: 0)').domain() == [
('char', 'ilike', 'foo%'),
['OR', ('char', 'ilike', 'bar%'), ('boolean', '=', False)]]
assert parser.parse(
'(char: foo or char: bar) Date Time: 12/4/2002').domain() == [
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
('datetime', '=', datetime.datetime(2002, 12, 4, 0, 0))]
assert parser.parse(
'char: foo or (char: bar and boolean: 1)').domain() == [
'OR', ('char', 'ilike', 'foo%'), [
('char', 'ilike', 'bar%'), ('boolean', '=', True)]]
assert parser.parse(
'char: foo and (char: bar or boolean: 0)').domain() == [
('char', 'ilike', 'foo%'),
['OR', ('char', 'ilike', 'bar%'), ('boolean', '=', False)]]
assert parser.parse('char: foo (char: bar or boolean: 0)').domain() == [
('char', 'ilike', 'foo%'),
['OR', ('char', 'ilike', 'bar%'), ('boolean', '=', False)]]
@@ -1338,6 +1380,7 @@ def test_composite_string():
['OR', ('char', 'ilike', 'bar%'), ('boolean', '=', False)]]) == \
'Char: foo (Char: bar or Boolean: False)'
def test_quote_complete():
parser = test_parser()
assert list(parser.parse('"char"').complete()) == ['Char:']
@@ -1348,12 +1391,14 @@ def test_quote_complete():
assert list(parser.parse('Char: "Boolean: false"').complete()) == [
'Char: "Boolean: false"']
def test_quote_domain():
parser = test_parser()
assert parser.parse('"char"').domain() == [('rec_name', 'ilike', 'char%')]
assert parser.parse('char: "foo bar"').domain() == [
('char', 'ilike', 'foo bar%')]
def test_quote_string():
parser = test_parser()
assert parser.string([('rec_name', 'ilike', 'char:%')]) == '"char:"'
@@ -1361,16 +1406,19 @@ def test_quote_string():
assert parser.string([('char', 'ilike', 'Boolean: false%')]) == \
'Char: "Boolean: false"'
def test_column():
parser = test_parser()
assert parser.parse(':').domain() == []
assert parser.parse('::').domain() == []
assert parser.parse(': foo').domain() == [('rec_name', 'ilike', 'foo%')]
assert parser.parse('foo :').domain() == [('rec_name', 'ilike', 'foo%')]
assert parser.parse(': foo b').domain() == [('rec_name', 'ilike', 'foo b%')]
assert parser.parse(': foo b').domain() == [
('rec_name', 'ilike', 'foo b%')]
assert parser.parse('": foo b"').domain() == [
('rec_name', 'ilike', ': foo b%')]
def test_double_dot():
parser = test_parser()
assert parser.parse('Integer: 0..42').domain() == [
@@ -1384,6 +1432,7 @@ def test_double_dot():
assert parser.parse('Integer: 0..42 Char: foo').domain() == [
('integer', '>=', 0), ('integer', '<', 42), ('char', 'ilike', 'foo%')]
def test_comma():
parser = test_parser()
assert parser.parse('Char: foo; bar').domain() == [
@@ -1409,6 +1458,7 @@ def test_comma():
assert parser.parse('Integer: 0; 1;; 2; 3').domain() == [
('integer', 'in', [0, 1, 0, 2, 3])]
def test_comparator():
parser = test_parser()
assert parser.parse('< foo').domain() == [
@@ -1430,6 +1480,7 @@ def test_comparator():
assert parser.parse('foo <').domain() == [
('rec_name', 'ilike', 'foo%'), ('rec_name', '<', '')]
def test_str():
parser = test_parser()
assert str(parser.parse('foo bar spam ham =')) == '''\
+7 -28
View File
@@ -15,11 +15,13 @@ from tryton.exceptions import TrytonError
_ = gettext.gettext
def get_home_dir():
if os.name == 'nt':
return os.path.join(os.environ['HOMEDRIVE'], os.environ['HOMEPATH'])
return os.environ['HOME']
def get_config_dir():
if os.name == 'nt':
return os.path.join(os.environ['APPDATA'], '.config', 'tryton',
@@ -29,20 +31,6 @@ def get_config_dir():
if not os.path.isdir(get_config_dir()):
os.makedirs(get_config_dir(), 0700)
def find_path(progs, args):
if os.name == 'nt':
return ''
if sys.platform == 'darwin':
return ''
paths = [x for x in os.environ['PATH'].split(':')
if os.path.isdir(x)]
for dir in paths:
for prog in progs:
val = os.path.join(dir, prog)
if os.path.isfile(val) or os.path.islink(val):
return val + ' ' + args
return ''
class ConfigManager(object):
"Config manager"
@@ -78,17 +66,6 @@ class ConfigManager(object):
'client.default_path': get_home_dir(),
'client.lang': locale.getdefaultlocale()[0],
'client.language_direction': 'ltr',
'client.actions': {
'odt': {0: find_path(['ooffice', 'ooffice2', 'libreoffice'], '"%s"'),
1: find_path(['ooffice', 'ooffice2', 'libreoffice'], '-p "%s"')},
'txt': {0: find_path(['ooffice', 'ooffice2', 'libreoffice'], '"%s"'),
1: find_path(['ooffice', 'ooffice2', 'libreoffice'], '-p "%s"')},
'pdf': {0: find_path(['evince', 'xpdf', 'gpdf',
'kpdf', 'epdfview', 'acroread'], '"%s"'), 1: ''},
'png': {0: find_path(['feh', 'display', 'qiv', 'eye'], '"%s"'), 1: ''},
'csv': {0: find_path(['ooffice', 'ooffice2', 'libreoffice'], '"%s"'),
1: find_path(['ooffice', 'ooffice2', 'libreoffice'], '-p "%s"')},
},
'client.email': '',
'client.can_change_accelerators': False,
'client.limit': 1000,
@@ -128,18 +105,19 @@ class ConfigManager(object):
if opt.config and not os.path.isfile(opt.config):
raise TrytonError(_('File "%s" not found') % (opt.config,))
self.rcfile = opt.config or os.path.join(get_config_dir(), 'tryton.conf')
self.rcfile = opt.config or os.path.join(
get_config_dir(), 'tryton.conf')
self.load()
for arg in ('log_level', 'log_logger'):
if getattr(opt, arg):
self.options['logging.'+arg[4:]] = getattr(opt, arg)
self.options['logging.' + arg[4:]] = getattr(opt, arg)
if opt.verbose:
self.options['logging.default'] = 'INFO'
for arg in ('login', 'port', 'server'):
if getattr(opt, arg):
self.options['login.'+arg] = getattr(opt, arg)
self.options['login.' + arg] = getattr(opt, arg)
def save(self):
try:
@@ -204,6 +182,7 @@ if not os.path.isdir(PIXMAPS_DIR):
TRYTON_ICON = gtk.gdk.pixbuf_new_from_file(
os.path.join(PIXMAPS_DIR, 'tryton-icon.png').encode('utf-8'))
def _data_dir():
data_dir = os.path.join(CURRENT_DIR, 'share', 'tryton')
if not os.path.isdir(data_dir):
+1 -2
View File
@@ -25,7 +25,6 @@ class Fingerprints(dict):
self[host] = sha1
def save(self):
lines = []
with open(KNOWN_HOSTS_PATH, 'w') as known_hosts:
known_hosts.writelines('%s %s' % (host, sha1)
+ os.linesep for host, sha1 in self.iteritems())
@@ -33,7 +32,7 @@ class Fingerprints(dict):
def __setitem__(self, key, value):
assert isinstance(key, basestring)
if value:
assert len(value) == 59 # len of formated sha1
assert len(value) == 59 # len of formated sha1
else:
value = ''
super(Fingerprints, self).__setitem__(key, value)
+71 -67
View File
@@ -15,15 +15,13 @@ except ImportError:
import json
import webbrowser
import tryton.rpc as rpc
from tryton.config import CONFIG, TRYTON_ICON, PIXMAPS_DIR, DATA_DIR, \
get_config_dir
from tryton.config import CONFIG, TRYTON_ICON, get_config_dir
import tryton.common as common
from tryton.action import Action
from tryton.exceptions import TrytonServerError, TrytonError
from tryton.gui.window import Window
from tryton.gui.window.preference import Preference
from tryton.gui.window import Limit
from tryton.gui.window import FileActions
from tryton.gui.window import Email
from tryton.gui.window.dblogin import DBLogin
from tryton.gui.window.dbcreate import DBCreate
@@ -32,12 +30,10 @@ from tryton.gui.window.tips import Tips
from tryton.gui.window.about import About
from tryton.gui.window.shortcuts import Shortcuts
from tryton.gui.window.dbrestore import DBRestore
import re
import tryton.translate as translate
import tryton.plugins
import pango
import time
import threading
try:
import gtk_osxapplication
except ImportError:
@@ -82,7 +78,8 @@ class Main(object):
self.macapp = None
if gtk_osxapplication is not None:
self.macapp = gtk_osxapplication.OSXApplication()
self.macapp.connect("NSApplicationBlockTermination", self.sig_close)
self.macapp.connect("NSApplicationBlockTermination",
self.sig_close)
gtk.accel_map_add_entry('<tryton>/File/Connect', gtk.keysyms.O,
gtk.gdk.CONTROL_MASK)
@@ -101,7 +98,7 @@ class Main(object):
gtk.accel_map_add_entry('<tryton>/Form/Save', gtk.keysyms.S,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Duplicate', gtk.keysyms.D,
gtk.gdk.CONTROL_MASK|gtk.gdk.SHIFT_MASK)
gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Delete', gtk.keysyms.D,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Next', gtk.keysyms.Page_Down,
@@ -112,10 +109,10 @@ class Main(object):
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Close', gtk.keysyms.W,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Previous Tab', gtk.keysyms.Page_Up,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Next Tab', gtk.keysyms.Page_Down,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Previous Tab',
gtk.keysyms.Page_Up, gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Next Tab',
gtk.keysyms.Page_Down, gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Goto', gtk.keysyms.G,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Reload', gtk.keysyms.R,
@@ -189,11 +186,6 @@ class Main(object):
self.sig_statusbar_show()
if os.name == 'nt' or sys.platform == 'darwin':
# Disable actions, on win32 we use os.startfile
# and on mac we use /usr/bin/open
self.menuitem_actions.set_sensitive(False)
# Adding a timer the check to requests
gobject.timeout_add(5 * 60 * 1000, self.request_set)
_MAIN.append(self)
@@ -259,6 +251,7 @@ class Main(object):
self.menuitem_shortcut = menuitem_shortcut
menubar.add(menuitem_shortcut)
menuitem_shortcut.set_accel_path('<tryton>/Shortcuts')
def shortcut_activate(widget):
if (not menuitem_shortcut.get_submenu()
or not menuitem_shortcut.get_submenu().get_children()):
@@ -276,7 +269,7 @@ class Main(object):
if self.macapp is not None:
self.menubar.set_no_show_all(True)
self.macapp.set_menu_bar(self.menubar)
self.macapp.insert_app_menu_item(self.aboutitem,0)
self.macapp.insert_app_menu_item(self.aboutitem, 0)
menuitem_file.show_all()
menuitem_user.show_all()
menuitem_options.show_all()
@@ -296,7 +289,8 @@ class Main(object):
if not update:
self.sb_username = gtk.Label()
self.sb_username.set_alignment(0.0, 0.5)
self.status_hbox.pack_start(self.sb_username, True, True, padding=5)
self.status_hbox.pack_start(self.sb_username, True, True,
padding=5)
self.sb_requests = gtk.Label()
self.sb_requests.set_alignment(0.5, 0.5)
@@ -310,14 +304,16 @@ class Main(object):
self.secure_img = gtk.Image()
self.secure_img.set_from_stock('tryton-lock', gtk.ICON_SIZE_MENU)
self.status_hbox.pack_start(self.secure_img, False, True, padding=2)
self.status_hbox.pack_start(self.secure_img, False, True,
padding=2)
self.status_hbox.show_all()
def _set_menu_file(self):
menu_file = gtk.Menu()
imagemenuitem_connect = gtk.ImageMenuItem(_('_Connect...'), self.accel_group)
imagemenuitem_connect = gtk.ImageMenuItem(_('_Connect...'),
self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-connect', gtk.ICON_SIZE_MENU)
imagemenuitem_connect.set_image(image)
@@ -351,7 +347,8 @@ class Main(object):
image.set_from_stock('tryton-folder-new', gtk.ICON_SIZE_MENU)
imagemenuitem_db_new.set_image(image)
imagemenuitem_db_new.connect('activate', self.sig_db_new)
imagemenuitem_db_new.set_accel_path('<tryton>/File/Database/New Database')
imagemenuitem_db_new.set_accel_path(
'<tryton>/File/Database/New Database')
menu_database.add(imagemenuitem_db_new)
imagemenuitem_db_restore = gtk.ImageMenuItem(_('_Restore Database...'))
@@ -359,7 +356,8 @@ class Main(object):
image.set_from_stock('tryton-folder-saved-search', gtk.ICON_SIZE_MENU)
imagemenuitem_db_restore.set_image(image)
imagemenuitem_db_restore.connect('activate', self.sig_db_restore)
imagemenuitem_db_restore.set_accel_path('<tryton>/File/Database/Restore Database')
imagemenuitem_db_restore.set_accel_path(
'<tryton>/File/Database/Restore Database')
menu_database.add(imagemenuitem_db_restore)
imagemenuitem_db_dump = gtk.ImageMenuItem(_('_Backup Database...'))
@@ -367,7 +365,8 @@ class Main(object):
image.set_from_stock('tryton-save-as', gtk.ICON_SIZE_MENU)
imagemenuitem_db_dump.set_image(image)
imagemenuitem_db_dump.connect('activate', self.sig_db_dump)
imagemenuitem_db_dump.set_accel_path('<tryton>/File/Database/Backup Database')
imagemenuitem_db_dump.set_accel_path(
'<tryton>/File/Database/Backup Database')
menu_database.add(imagemenuitem_db_dump)
imagemenuitem_db_drop = gtk.ImageMenuItem(_('Dro_p Database...'))
@@ -375,11 +374,12 @@ class Main(object):
image.set_from_stock('tryton-delete', gtk.ICON_SIZE_MENU)
imagemenuitem_db_drop.set_image(image)
imagemenuitem_db_drop.connect('activate', self.sig_db_drop)
imagemenuitem_db_drop.set_accel_path('<tryton>/File/Database/Drop Database')
imagemenuitem_db_drop.set_accel_path(
'<tryton>/File/Database/Drop Database')
menu_database.add(imagemenuitem_db_drop)
imagemenuitem_close = gtk.ImageMenuItem(_('_Quit...'), self.accel_group)
imagemenuitem_close = gtk.ImageMenuItem(_('_Quit...'),
self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-log-out', gtk.ICON_SIZE_MENU)
imagemenuitem_close.set_image(image)
@@ -404,7 +404,8 @@ class Main(object):
menu_user.add(gtk.SeparatorMenuItem())
imagemenuitem_menu = gtk.ImageMenuItem(_('_Menu Reload'), self.accel_group)
imagemenuitem_menu = gtk.ImageMenuItem(_('_Menu Reload'),
self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-start-here', gtk.ICON_SIZE_MENU)
imagemenuitem_menu.set_image(image)
@@ -414,11 +415,11 @@ class Main(object):
imagemenuitem_menu_toggle = gtk.ImageMenuItem(_('_Menu Toggle'),
self.accel_group)
imagemenuitem_menu_toggle.connect('activate', lambda *a: self.menu_toggle())
imagemenuitem_menu_toggle.connect('activate',
lambda *a: self.menu_toggle())
imagemenuitem_menu_toggle.set_accel_path('<tryton>/User/Menu Toggle')
menu_user.add(imagemenuitem_menu_toggle)
imagemenuitem_home = gtk.ImageMenuItem(_('_Home'), self.accel_group)
image = gtk.Image()
image.set_from_stock('tryton-go-home', gtk.ICON_SIZE_MENU)
@@ -434,7 +435,8 @@ class Main(object):
image.set_from_stock('tryton-mail-message-new', gtk.ICON_SIZE_MENU)
imagemenuitem_send_request.set_image(image)
imagemenuitem_send_request.connect('activate', self.sig_request_new)
imagemenuitem_send_request.set_accel_path('<tryton>/User/Send a Request')
imagemenuitem_send_request.set_accel_path(
'<tryton>/User/Send a Request')
menu_user.add(imagemenuitem_send_request)
imagemenuitem_open_request = gtk.ImageMenuItem(_('_Read my Requests'))
@@ -442,7 +444,8 @@ class Main(object):
image.set_from_stock('tryton-mail-message', gtk.ICON_SIZE_MENU)
imagemenuitem_open_request.set_image(image)
imagemenuitem_open_request.connect('activate', self.sig_request_open)
imagemenuitem_open_request.set_accel_path('<tryton>/User/Read my Requests')
imagemenuitem_open_request.set_accel_path(
'<tryton>/User/Read my Requests')
menu_user.add(imagemenuitem_open_request)
return menu_user
@@ -460,7 +463,8 @@ class Main(object):
radiomenuitem_default = gtk.RadioMenuItem(label=_('_Default'))
radiomenuitem_default.connect('activate',
lambda x: self.sig_toolbar('default'))
radiomenuitem_default.set_accel_path('<tryton>/Options/Toolbar/Default')
radiomenuitem_default.set_accel_path(
'<tryton>/Options/Toolbar/Default')
menu_toolbar.add(radiomenuitem_default)
if (CONFIG['client.toolbar'] or 'both') == 'default':
radiomenuitem_default.set_active(True)
@@ -543,18 +547,20 @@ class Main(object):
checkmenuitem_statusbar = gtk.CheckMenuItem(_('Statusbar'))
checkmenuitem_statusbar.connect('activate',
lambda menuitem: self.sig_statusbar_change(menuitem.get_active()))
checkmenuitem_statusbar.set_accel_path('<tryton>/Options/Form/Statusbar')
lambda menuitem: self.sig_statusbar_change(menuitem.get_active()))
checkmenuitem_statusbar.set_accel_path(
'<tryton>/Options/Form/Statusbar')
menu_form.add(checkmenuitem_statusbar)
if CONFIG['form.statusbar']:
checkmenuitem_statusbar.set_active(True)
checkmenuitem_save_width_height = gtk.CheckMenuItem(_('Save Width/Height'))
checkmenuitem_save_width_height = gtk.CheckMenuItem(
_('Save Width/Height'))
checkmenuitem_save_width_height.connect('activate',
lambda menuitem: CONFIG.__setitem__('client.save_width_height',
menuitem.get_active()))
lambda menuitem: CONFIG.__setitem__('client.save_width_height',
menuitem.get_active()))
checkmenuitem_save_width_height.set_accel_path(
'<tryton>/Options/Form/Save Width Height')
'<tryton>/Options/Form/Save Width Height')
menu_form.add(checkmenuitem_save_width_height)
if CONFIG['client.save_width_height']:
checkmenuitem_save_width_height.set_active(True)
@@ -602,7 +608,8 @@ class Main(object):
label=_('Left'))
radiomenuitem_left.connect('activate',
lambda x: CONFIG.__setitem__('client.form_tab', 'left'))
radiomenuitem_left.set_accel_path('<tryton>/Options/Tabs Position/Left')
radiomenuitem_left.set_accel_path(
'<tryton>/Options/Tabs Position/Left')
menu_tab.add(radiomenuitem_left)
if (CONFIG['client.form_tab'] or 'left') == 'left':
radiomenuitem_left.set_active(True)
@@ -611,7 +618,8 @@ class Main(object):
label=_('Right'))
radiomenuitem_right.connect('activate',
lambda x: CONFIG.__setitem__('client.form_tab', 'right'))
radiomenuitem_right.set_accel_path('<tryton>/Options/Tabs Position/Right')
radiomenuitem_right.set_accel_path(
'<tryton>/Options/Tabs Position/Right')
menu_tab.add(radiomenuitem_right)
if (CONFIG['client.form_tab'] or 'left') == 'right':
radiomenuitem_right.set_active(True)
@@ -620,7 +628,8 @@ class Main(object):
label=_('Bottom'))
radiomenuitem_bottom.connect('activate',
lambda x: CONFIG.__setitem__('client.form_tab', 'bottom'))
radiomenuitem_bottom.set_accel_path('<tryton>/Options/Tabs Position/Bottom')
radiomenuitem_bottom.set_accel_path(
'<tryton>/Options/Tabs Position/Bottom')
menu_tab.add(radiomenuitem_bottom)
if (CONFIG['client.form_tab'] or 'left') == 'bottom':
radiomenuitem_bottom.set_active(True)
@@ -631,12 +640,6 @@ class Main(object):
menuitem_limit.set_accel_path('<tryton>/Options/Search Limit')
menu_options.add(menuitem_limit)
menuitem_actions = gtk.MenuItem(_('File _Actions...'))
self.menuitem_actions = menuitem_actions
menuitem_actions.connect('activate', self.sig_file_actions)
menuitem_actions.set_accel_path('<tryton>/Options/File Actions')
menu_options.add(menuitem_actions)
menuitem_email = gtk.MenuItem(_('_Email...'))
self.menuitem_email = menuitem_email
menuitem_email.connect('activate', self.sig_email)
@@ -657,13 +660,15 @@ class Main(object):
def _set_menu_plugins(self):
menu_plugins = gtk.Menu()
imagemenuitem_plugin_execute = gtk.ImageMenuItem(_('_Execute a Plugin'))
imagemenuitem_plugin_execute = gtk.ImageMenuItem(
_('_Execute a Plugin'))
image = gtk.Image()
image.set_from_stock('tryton-executable', gtk.ICON_SIZE_MENU)
imagemenuitem_plugin_execute.set_image(image)
imagemenuitem_plugin_execute.connect('activate', self.sig_plugin_execute)
imagemenuitem_plugin_execute.connect('activate',
self.sig_plugin_execute)
imagemenuitem_plugin_execute.set_accel_path(
'<tryton>/Plugins/Execute a Plugin')
'<tryton>/Plugins/Execute a Plugin')
menu_plugins.add(imagemenuitem_plugin_execute)
return menu_plugins
@@ -678,15 +683,16 @@ class Main(object):
imagemenuitem_tips.set_accel_path('<tryton>/Help/Tips')
menu_help.add(imagemenuitem_tips)
imagemenuitem_shortcuts = gtk.ImageMenuItem(_('_Keyboard Shortcuts...'))
imagemenuitem_shortcuts = gtk.ImageMenuItem(
_('_Keyboard Shortcuts...'))
image = gtk.Image()
image.set_from_stock('tryton-help', gtk.ICON_SIZE_MENU)
imagemenuitem_shortcuts.set_image(image)
imagemenuitem_shortcuts.connect('activate', self.sig_shortcuts)
imagemenuitem_shortcuts.set_accel_path('<tryton>/Help/Keyboard Shortcuts')
imagemenuitem_shortcuts.set_accel_path(
'<tryton>/Help/Keyboard Shortcuts')
menu_help.add(imagemenuitem_shortcuts)
imagemenuitem_about = gtk.ImageMenuItem(_('_About...'))
image = gtk.Image()
image.set_from_stock('gtk-about', gtk.ICON_SIZE_MENU)
@@ -817,9 +823,6 @@ class Main(object):
def sig_limit(self, widget):
Limit().run()
def sig_file_actions(self, widget):
FileActions().run()
def sig_email(self, widget):
Email().run()
@@ -843,7 +846,8 @@ class Main(object):
'get_preferences', False, rpc.CONTEXT)
if prefs and 'language_direction' in prefs:
translate.set_language_direction(prefs['language_direction'])
CONFIG['client.language_direction'] = prefs['language_direction']
CONFIG['client.language_direction'] = \
prefs['language_direction']
self.sb_username.set_text(prefs.get('status_bar', ''))
if prefs and 'language' in prefs:
translate.setlang(prefs['language'], prefs.get('locale'))
@@ -866,8 +870,8 @@ class Main(object):
ctx = {}
ctx.update(rpc.CONTEXT)
ctx['active_test'] = False
return Window.create(None, 'res.request', False, [ ],
mode=['form', 'tree'], context=ctx)
return Window.create(None, 'res.request', False, [],
mode=['form', 'tree'], context=ctx)
def sig_request_open(self, widget):
ctx = {}
@@ -881,8 +885,8 @@ class Main(object):
else:
raise
ids = ids1 + ids2
return Window.create(False, 'res.request', ids, [ ],
mode=['tree', 'form'], context=ctx)
return Window.create(False, 'res.request', ids, [],
mode=['tree', 'form'], context=ctx)
def request_set(self, exception=False):
try:
@@ -897,7 +901,6 @@ class Main(object):
else:
ids, ids2 = rpc.execute('model', 'res.request', 'request_get',
rpc.CONTEXT)
label = _('Requests (%s/%s)') % (len(ids), len(ids2))
message = _('Waiting requests: %s received - %s sent') % (len(ids),
len(ids2))
self.sb_requests.set_text(message)
@@ -935,7 +938,8 @@ class Main(object):
common.ICONFACTORY.load_icons()
if prefs and 'language_direction' in prefs:
translate.set_language_direction(prefs['language_direction'])
CONFIG['client.language_direction'] = prefs['language_direction']
CONFIG['client.language_direction'] = \
prefs['language_direction']
menu_id = self.sig_win_menu(quiet=False, prefs=prefs)
if menu_id:
self.sig_home_new(quiet=True, except_id=menu_id, prefs=prefs)
@@ -1026,7 +1030,8 @@ class Main(object):
if (self.menu_screen
and self.menu_screen.current_view.view_type == 'tree'):
try:
has_focus = self.menu_screen.current_view.widget_tree.has_focus()
has_focus = \
self.menu_screen.current_view.widget_tree.has_focus()
except AttributeError:
has_focus = (self.menu_screen.current_view.widget_tree.flags()
& gtk.HAS_FOCUS)
@@ -1114,7 +1119,7 @@ class Main(object):
def sig_configure(self, widget, event):
if hasattr(event, 'width') \
and hasattr(event, 'height'):
self._width = int(event.width)
self._width = int(event.width)
self._height = int(event.height)
return False
@@ -1336,7 +1341,7 @@ class Main(object):
if dbname:
with open(filename, 'rb') as file_p:
data = file_p.read()
host, port = url.rsplit(':' , 1)
host, port = url.rsplit(':', 1)
rpcprogress = common.RPCProgress('db_exec', (host, int(port),
'restore', dbname, passwd, buffer(data), update))
try:
@@ -1497,7 +1502,6 @@ class Main(object):
direct_print = json.loads(params.get('direct_print', 'false'))
email_print = json.loads(params.get('email_print', 'false'))
email = json.loads(params.get('email', 'null'))
name = json.loads(params.get('name', 'false'))
context = json.loads(params.get('context', '{}'))
except ValueError:
return
-1
View File
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from window import *
from fileactions import *
from email import *
from limit import *
+1 -2
View File
@@ -3,9 +3,8 @@
#this repository contains the full copyright notices and license terms.
import gtk
import gettext
import os
import webbrowser
from tryton.config import TRYTON_ICON, PIXMAPS_DIR, DATA_DIR
from tryton.config import TRYTON_ICON
from tryton.version import VERSION, WEBSITE
from tryton.common import get_toplevel_window
+15 -8
View File
@@ -5,20 +5,27 @@ from tryton.gui.window.view_form.screen import Screen
from tryton.gui.window.win_form import WinForm
class Attachment(object):
class Attachment(WinForm):
"Attachment window"
def __init__(self, model_name, record_id):
def __init__(self, model_name, record_id, callback=None):
self.resource = '%s,%s' % (model_name, record_id)
def run(self):
self.attachment_callback = callback
screen = Screen('ir.attachment', domain=[
('resource', '=', self.resource),
], mode=['tree', 'form'], context={
'resource': self.resource,
}, exclude_field='resource')
screen.search_filter()
def callback(result):
if result:
screen.group.save()
WinForm(screen, callback, view_type='tree')
super(Attachment, self).__init__(screen, self.callback,
view_type='tree')
def destroy(self):
self.prev_view.save_width_height()
super(Attachment, self).destroy()
def callback(self, result):
if result:
self.screen.group.save()
if self.attachment_callback:
self.attachment_callback()
-2
View File
@@ -2,8 +2,6 @@
#this repository contains the full copyright notices and license terms.
"Board"
import gettext
import gtk
from tryton.config import CONFIG
from tryton.signal_event import SignalEvent
from tryton.gui import Main
import tryton.rpc as rpc
+63 -62
View File
@@ -5,7 +5,7 @@ import gobject
import gettext
import re
import tryton.common as common
from tryton.config import CONFIG, TRYTON_ICON, PIXMAPS_DIR
from tryton.config import CONFIG, TRYTON_ICON
from tryton.exceptions import TrytonServerError
import tryton.rpc as rpc
@@ -28,10 +28,11 @@ class DBCreate(object):
self.entry_adminpasswd2.set_sensitive(True)
self.entry_server_connection.modify_text(gtk.STATE_INSENSITIVE, \
gtk.gdk.color_parse(common.COLOR_SCHEMES["black"]))
self.tooltips.set_tip(self.entry_server_connection,_("This is the URL of " \
"the Tryton server. Use server 'localhost' and port '8000' if " \
"the server is installed on this computer. Click on 'Change' to " \
"change the address."))
self.tooltips.set_tip(self.entry_server_connection,
_("This is the URL of the Tryton server. Use server "
"'localhost' and port '8000' if the server is installed "
"on this computer. Click on 'Change' to change the "
"address."))
else:
self.entry_serverpasswd.set_sensitive(False)
self.entry_dbname.set_sensitive(False)
@@ -89,12 +90,12 @@ class DBCreate(object):
must be filled, then the Create button is set to sensitive. This
event method doesn't check the valid of single entrys.
"""
if self.entry_server_connection.get_text() != "" \
and self.entry_serverpasswd.get_text() != "" \
and self.entry_dbname.get_text() != "" \
and self.combo_language.get_active() != -1 \
and self.entry_adminpasswd.get_text() != "" \
and self.entry_adminpasswd2.get_text() != "":
if (self.entry_server_connection.get_text() != ""
and self.entry_serverpasswd.get_text() != ""
and self.entry_dbname.get_text() != ""
and self.combo_language.get_active() != -1
and self.entry_adminpasswd.get_text() != ""
and self.entry_adminpasswd2.get_text() != ""):
widget.unset_flags(gtk.HAS_DEFAULT)
self.button_create.set_sensitive(True)
self.button_create.set_flags(gtk.CAN_DEFAULT)
@@ -114,7 +115,7 @@ class DBCreate(object):
- First character must be a letter
"""
if (new_text.isalnum() or new_text == '_' ):
if new_text.isalnum() or new_text == '_':
_hid = entry.get_data('handlerid')
entry.handler_block(_hid)
_pos = entry.get_position()
@@ -133,11 +134,9 @@ class DBCreate(object):
self.port = port
# GTK Stuffs
self.dialog = gtk.Dialog(
title= _("Create new database"),
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
| gtk.WIN_POS_CENTER_ON_PARENT,
)
self.dialog = gtk.Dialog(title=_("Create new database"),
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |
gtk.WIN_POS_CENTER_ON_PARENT)
self.dialog.set_has_separator(True)
self.dialog.set_icon(TRYTON_ICON)
# This event is needed for controlling the button_create
@@ -152,7 +151,8 @@ class DBCreate(object):
img_connect = gtk.Image()
img_connect.set_from_stock('tryton-new', gtk.ICON_SIZE_BUTTON)
self.button_create.set_image(img_connect)
self.tooltips.set_tip(self.button_create, _('Create the new database.'))
self.tooltips.set_tip(self.button_create,
_('Create the new database.'))
self.dialog.add_action_widget(self.button_create, gtk.RESPONSE_OK)
self.dialog.set_default_response(gtk.RESPONSE_OK)
@@ -163,10 +163,10 @@ class DBCreate(object):
table.set_col_spacings(3)
self.label_server_setup = gtk.Label()
self.label_server_setup.set_markup("<b>"+ _("Server Setup:")+ "</b>")
self.label_server_setup.set_markup("<b>" + _("Server Setup:") + "</b>")
self.label_server_setup.set_justify(gtk.JUSTIFY_LEFT)
self.label_server_setup.set_alignment(0, 1)
self.label_server_setup.set_padding( 9, 5)
self.label_server_setup.set_padding(9, 5)
table.attach(self.label_server_setup, 0, 3, 0, 1, xoptions=gtk.FILL)
self.label_server = gtk.Label(_("Server connection:"))
self.label_server.set_alignment(1, 0.5)
@@ -176,25 +176,26 @@ class DBCreate(object):
self.entry_server_connection.set_sensitive(False)
self.entry_server_connection.unset_flags(gtk.CAN_FOCUS)
self.entry_server_connection.set_editable(False)
self.tooltips.set_tip(self.entry_server_connection, _("This is the URL of " \
"the server. Use server 'localhost' and port '8000' if " \
"the server is installed on this computer. Click on 'Change' to " \
"change the address."))
self.tooltips.set_tip(self.entry_server_connection,
_("This is the URL of the server. Use server 'localhost' and port "
"'8000' if the server is installed on this computer. Click on "
"'Change' to change the address."))
self.button_server_change = gtk.Button(_("C_hange"), stock=None,
use_underline=True)
img_button_server_change = gtk.Image()
img_button_server_change.set_from_stock('tryton-preferences-system', \
img_button_server_change.set_from_stock('tryton-preferences-system',
gtk.ICON_SIZE_BUTTON)
self.button_server_change.set_image(img_button_server_change)
table.attach(self.button_server_change, 2, 3, 1, 2, yoptions=False, xoptions=gtk.FILL)
self.tooltips.set_tip(self.button_server_change, _("Setup the " \
"server connection..."))
table.attach(self.button_server_change, 2, 3, 1, 2, yoptions=False,
xoptions=gtk.FILL)
self.tooltips.set_tip(self.button_server_change,
_("Setup the server connection..."))
table.attach(self.entry_server_connection, 1, 2, 1, 2)
self.label_serverpasswd = gtk.Label(_("Tryton Server Password:"))
self.label_serverpasswd.set_justify(gtk.JUSTIFY_RIGHT)
self.label_serverpasswd.set_alignment(1, 0.5)
self.label_serverpasswd.set_padding( 3, 3)
self.label_serverpasswd.set_padding(3, 3)
table.attach(self.label_serverpasswd, 0, 1, 2, 3, xoptions=gtk.FILL,
yoptions=gtk.FILL)
self.entry_serverpasswd = gtk.Entry()
@@ -212,15 +213,15 @@ class DBCreate(object):
table.attach(self.hseparator, 0, 3, 3, 4, yoptions=gtk.FILL)
label_dbname = gtk.Label()
label_dbname.set_markup("<b>" + _("New database setup:") + "</b>")
label_dbname.set_markup("<b>" + _("New database setup:") + "</b>")
label_dbname.set_justify(gtk.JUSTIFY_LEFT)
label_dbname.set_alignment(0, 1)
label_dbname.set_padding( 9, 5)
label_dbname.set_padding(9, 5)
table.attach(label_dbname, 0, 3, 4, 5, xoptions=gtk.FILL,
yoptions=gtk.FILL)
label_dbname = gtk.Label(_("Database name:"))
label_dbname.set_justify(gtk.JUSTIFY_RIGHT)
label_dbname.set_padding( 3, 3)
label_dbname.set_padding(3, 3)
label_dbname.set_alignment(1, 0.5)
table.attach(label_dbname, 0, 1, 5, 6, xoptions=gtk.FILL,
yoptions=gtk.FILL)
@@ -229,18 +230,18 @@ class DBCreate(object):
self.entry_dbname.set_width_chars(16)
self.entry_dbname.set_activates_default(True)
table.attach(self.entry_dbname, 1, 3, 5, 6, yoptions=gtk.FILL)
self.tooltips.set_tip(self.entry_dbname, _("Choose the name of the new " \
"database.\n" \
"Allowed characters are alphanumerical or _ (underscore)\n" \
"You need to avoid all accents, space or special characters! " \
"Example: tryton"))
handlerid = self.entry_dbname.connect("insert-text", \
self.tooltips.set_tip(self.entry_dbname,
_("Choose the name of the new database.\n"
"Allowed characters are alphanumerical or _ (underscore)\n"
"You need to avoid all accents, space or special characters! "
"Example: tryton"))
handlerid = self.entry_dbname.connect("insert-text",
self.entry_insert_text)
self.entry_dbname.set_data('handlerid', handlerid)
label_language = gtk.Label(_("Default language:"))
label_language.set_justify(gtk.JUSTIFY_RIGHT)
label_language.set_alignment(1, 0.5)
label_language.set_padding( 3, 3)
label_language.set_padding(3, 3)
table.attach(label_language, 0, 1, 6, 7, xoptions=gtk.FILL,
yoptions=gtk.FILL)
eventbox_language = gtk.EventBox()
@@ -253,24 +254,25 @@ class DBCreate(object):
"the administration menu."))
label_adminpasswd = gtk.Label(_("Admin password:"))
label_adminpasswd.set_justify(gtk.JUSTIFY_RIGHT)
label_adminpasswd.set_padding( 3, 3)
label_adminpasswd.set_padding(3, 3)
label_adminpasswd.set_alignment(1, 0.5)
table.attach(label_adminpasswd, 0, 1, 7, 8, xoptions=gtk.FILL,
yoptions=gtk.FILL)
self.entry_adminpasswd = gtk.Entry()
self.entry_adminpasswd.set_visibility(False)
self.entry_adminpasswd.set_activates_default(True)
self.tooltips.set_tip(self.entry_adminpasswd, _("Choose a password for " \
"the admin user of the new database. With these credentials you " \
"will be later able to login into the database:\n" \
"User name: admin\n" \
"Password: <The password you set here>"))
self.tooltips.set_tip(self.entry_adminpasswd,
_("Choose a password for the admin user of the new database. "
"With these credentials you will be later able to login into "
"the database:\n"
"User name: admin\n"
"Password: <The password you set here>"))
table.attach(self.entry_adminpasswd, 1, 3, 7, 8, yoptions=gtk.FILL)
self.entry_adminpasswd.connect("key-press-event", \
self.event_passwd_clear)
label_adminpasswd2 = gtk.Label(_("Confirm admin password:"))
label_adminpasswd2.set_justify(gtk.JUSTIFY_RIGHT)
label_adminpasswd2.set_padding( 3, 3)
label_adminpasswd2.set_padding(3, 3)
label_adminpasswd2.set_alignment(1, 0.5)
table.attach(label_adminpasswd2, 0, 1, 8, 9, xoptions=gtk.FILL,
yoptions=gtk.FILL)
@@ -355,14 +357,14 @@ class DBCreate(object):
common.process_exception(exception)
continue
if exist:
common.warning(_("A database with the same name " \
"already exists.\n" \
"Try another database name."),
_("This database name already exist!"))
self.entry_dbname.set_text("")
self.entry_dbname.grab_focus()
continue
else: # Everything runs fine, break the block here
common.warning(_("A database with the same name "
"already exists.\n"
"Try another database name."),
_("This database name already exist!"))
self.entry_dbname.set_text("")
self.entry_dbname.grab_focus()
continue
else: # Everything runs fine, break the block here
host = url_m.group(1)
port = url_m.group(2)
try:
@@ -378,13 +380,13 @@ class DBCreate(object):
self.entry_serverpasswd.set_text("")
self.entry_serverpasswd.grab_focus()
continue
else: # Unclassified error
common.warning(_("Can't create the " \
"database, caused by an unknown reason.\n" \
"If there is a database created, it could " \
"be broken. Maybe drop this database! " \
"Please check the error message for " \
"possible informations.\n" \
else: # Unclassified error
common.warning(_("Can't create the "
"database, caused by an unknown reason.\n"
"If there is a database created, it could "
"be broken. Maybe drop this database! "
"Please check the error message for "
"possible informations.\n"
"Error message:\n")
+ str(exception.faultCode),
_("Error creating database!"))
@@ -410,4 +412,3 @@ class DBCreate(object):
parent.present()
self.dialog.destroy()
return dbname
+17 -16
View File
@@ -1,7 +1,5 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of this
# repository contains the full copyright notices and license terms.
import threading
import gtk
import gobject
import gettext
@@ -11,6 +9,7 @@ import tryton.rpc as rpc
_ = gettext.gettext
class DBBackupDrop(object):
"""
Widget for database backup and drop.
@@ -20,14 +19,16 @@ class DBBackupDrop(object):
db_widget.hide()
label.hide()
dbprogress = common.DBProgress(host, port)
def callback(dbs, createdb):
if dbs is None or dbs == -1:
if dbs is None:
label.set_label('<b>' + \
_('Could not connect to server!') + '</b>')
label.set_label('<b>' +
_('Could not connect to server!') + '</b>')
else:
label.set_label('<b>' + \
_('This client version is not compatible with the server!')
label.set_label('<b>' +
_('This client version is not compatible '
'with the server!')
+ '</b>')
db_widget.hide()
label.show()
@@ -58,9 +59,9 @@ class DBBackupDrop(object):
must be filled, then the Create button is set to sensitive. This
event method doesn't check the valid of single entrys.
"""
if self.entry_server_connection.get_text() != "" \
and self.combo_database.get_active_text() != "" \
and self.entry_serverpasswd.get_text() != "":
if (self.entry_server_connection.get_text() != ""
and self.combo_database.get_active_text() != ""
and self.entry_serverpasswd.get_text() != ""):
widget.unset_flags(gtk.HAS_DEFAULT)
self.button_ok.set_sensitive(True)
self.button_ok.set_flags(gtk.CAN_DEFAULT)
@@ -73,7 +74,7 @@ class DBBackupDrop(object):
def __init__(self, function=None):
# This widget is used for creating and droping a database!
if function =="backup":
if function == "backup":
dialog_title = _("Backup a database")
button_ok_text = _("Backup")
button_ok_tooltip = _("Backup the choosen database.")
@@ -114,14 +115,15 @@ class DBBackupDrop(object):
table.set_border_width(10)
table.set_row_spacings(3)
table.set_col_spacings(3)
self.dialog_vbox.pack_start(table, True, False,0)
self.dialog_vbox.pack_start(table, True, False, 0)
label_subtitle = gtk.Label()
label_subtitle.set_markup("<b>"+ label_subtitle_text + "</b>")
label_subtitle.set_markup("<b>" + label_subtitle_text + "</b>")
label_subtitle.set_justify(gtk.JUSTIFY_LEFT)
label_subtitle.set_alignment(0, 1)
label_subtitle.set_padding( 9, 5)
table.attach(label_subtitle, 0, 3, 0, 1, yoptions=False, xoptions=gtk.FILL)
label_subtitle.set_padding(9, 5)
table.attach(label_subtitle, 0, 3, 0, 1, yoptions=False,
xoptions=gtk.FILL)
hseparator = gtk.HSeparator()
table.attach(hseparator, 0, 3, 1, 2, yoptions=False)
@@ -183,7 +185,7 @@ class DBBackupDrop(object):
self.label_serverpasswd = gtk.Label(_("Tryton Server Password:"))
self.label_serverpasswd.set_justify(gtk.JUSTIFY_RIGHT)
self.label_serverpasswd.set_alignment(1, 0.5)
self.label_serverpasswd.set_padding( 3, 3)
self.label_serverpasswd.set_padding(3, 3)
table.attach(self.label_serverpasswd, 0, 1, 4, 5, yoptions=False,
xoptions=gtk.FILL)
@@ -234,4 +236,3 @@ class DBBackupDrop(object):
self.dialog.destroy()
return (url, database, passwd)
+19 -21
View File
@@ -5,10 +5,7 @@ import ConfigParser
import gtk
import gobject
import os
import re
import gettext
import threading
import time
from tryton.version import VERSION
import tryton.common as common
@@ -31,8 +28,9 @@ class DBListEditor(object):
# GTK Stuffs
self.parent = parent
self.dialog = gtk.Dialog(title=_(u'Profile Editor'), parent=parent,
flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
self.ok_button = self.dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
self.ok_button = self.dialog.add_button(gtk.STOCK_OK,
gtk.RESPONSE_ACCEPT)
self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
self.dialog.set_has_separator(True)
self.dialog.set_icon(TRYTON_ICON)
@@ -94,7 +92,8 @@ class DBListEditor(object):
database.set_padding(3, 3)
self.database_entry = gtk.Entry()
self.database_entry.connect('changed', self.dbentry_changed)
self.database_entry.connect('changed', self.update_profiles, 'database')
self.database_entry.connect('changed', self.update_profiles,
'database')
self.database_entry.set_activates_default(True)
self.database_label = gtk.Label()
self.database_label.set_use_markup(True)
@@ -108,7 +107,7 @@ class DBListEditor(object):
self.database_combo.connect('changed', self.dbcombo_changed)
self.database_button = gtk.Button(_(u'Create'))
self.database_button.connect('clicked', self.db_create)
self.database_progressbar= gtk.ProgressBar()
self.database_progressbar = gtk.ProgressBar()
self.database_progressbar.set_text(_(u'Fetching databases list'))
image = gtk.Image()
image.set_from_stock('tryton-new', gtk.ICON_SIZE_BUTTON)
@@ -131,7 +130,8 @@ class DBListEditor(object):
username.set_alignment(1, 0.5)
username.set_padding(3, 3)
self.username_entry = gtk.Entry()
self.username_entry.connect('changed', self.update_profiles, 'username')
self.username_entry.connect('changed', self.update_profiles,
'username')
self.username_entry.set_activates_default(True)
table.attach(username, 0, 1, 4, 5, yoptions=False, xoptions=gtk.FILL)
table.attach(self.username_entry, 1, 2, 4, 5, yoptions=False)
@@ -193,10 +193,10 @@ class DBListEditor(object):
def profile_create(self, button):
self.clear_entries()
model = self.profile_tree.get_model()
selection = self.profile_tree.get_selection()
model.append(['', False])
column = self.profile_tree.get_column(0)
self.profile_tree.set_cursor(len(model)-1, column, start_editing=True)
self.profile_tree.set_cursor(len(model) - 1, column,
start_editing=True)
def profile_delete(self, button):
self.clear_entries()
@@ -375,7 +375,7 @@ class DBLogin(object):
# GTK Stuffs
self.parent = common.get_toplevel_window()
self.dialog = gtk.Dialog(title=_('Login'), parent=self.parent,
flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
self.dialog.set_has_separator(True)
self.dialog.set_icon(TRYTON_ICON)
@@ -444,7 +444,8 @@ class DBLogin(object):
self.label_host.set_alignment(1, 0.5)
self.label_host.set_padding(3, 3)
self.entry_host = gtk.Entry()
self.entry_host.connect_after('focus-out-event', self.clear_profile_combo)
self.entry_host.connect_after('focus-out-event',
self.clear_profile_combo)
self.entry_host.set_activates_default(True)
self.table_main.attach(self.label_host, 0, 1, 4, 5, xoptions=gtk.FILL)
self.table_main.attach(self.entry_host, 1, 3, 4, 5)
@@ -453,7 +454,8 @@ class DBLogin(object):
self.label_database.set_alignment(1, 0.5)
self.label_database.set_padding(3, 3)
self.entry_database = gtk.Entry()
self.entry_database.connect_after('focus-out-event', self.clear_profile_combo)
self.entry_database.connect_after('focus-out-event',
self.clear_profile_combo)
self.entry_database.set_activates_default(True)
self.table_main.attach(self.label_database, 0, 1, 5, 6,
xoptions=gtk.FILL)
@@ -465,12 +467,12 @@ class DBLogin(object):
self.entry_login = gtk.Entry()
self.entry_login.set_activates_default(True)
self.table_main.attach(self.entry_login, 1, 3, 6, 7)
label_password = gtk.Label(str = _("Password:"))
label_password = gtk.Label(str=_("Password:"))
label_password.set_justify(gtk.JUSTIFY_RIGHT)
label_password.set_alignment(1, 0.5)
label_password.set_padding(3, 3)
self.table_main.attach(label_password, 0, 1, 7, 8, xoptions=gtk.FILL)
label_username = gtk.Label(str = _("User name:"))
label_username = gtk.Label(str=_("User name:"))
label_username.set_alignment(1, 0.5)
label_username.set_padding(3, 3)
self.table_main.attach(label_username, 0, 1, 6, 7, xoptions=gtk.FILL)
@@ -631,13 +633,10 @@ class DBLogin(object):
port = self.get_port(netloc)
except ValueError:
continue
if ':' in host:
hostname = '[%s]' % host
else:
hostname = host
try:
if not common.test_server_version(host, port):
common.warning('', _(u'Incompatible version of the server'))
common.warning('',
_(u'Incompatible version of the server'))
continue
except Exception, exception:
common.process_exception(exception)
@@ -660,4 +659,3 @@ class DBLogin(object):
Main.get_main().refresh_ssl()
raise TrytonError('QueryCanceled')
return result
+9 -7
View File
@@ -5,7 +5,7 @@ import gtk
import gobject
import gettext
import tryton.common as common
from tryton.config import CONFIG, TRYTON_ICON
from tryton.config import CONFIG
import tryton.rpc as rpc
_ = gettext.gettext
@@ -19,10 +19,10 @@ class DBRestore(object):
must be filled, then the restore button is set to sensitive. This
event method doesn't check the valid of single entrys.
"""
if self.entry_server_url.get_text() != "" \
and self.entry_server_password.get_text() != "" \
and self.entry_server_url.get_text() != "" \
and self.entry_db_name.get_text() != "":
if (self.entry_server_url.get_text() != ""
and self.entry_server_password.get_text() != ""
and self.entry_server_url.get_text() != ""
and self.entry_db_name.get_text() != ""):
widget.unset_flags(gtk.HAS_DEFAULT)
self.button_restore.set_sensitive(True)
self.button_restore.set_flags(gtk.CAN_DEFAULT)
@@ -40,7 +40,7 @@ class DBRestore(object):
- Allowed characters are alpha-nummeric [A-Za-z0-9] and underscore (_)
- First character must be a letter
"""
if (new_text.isalnum() or new_text == '_' ):
if new_text.isalnum() or new_text == '_':
_hid = entry.get_data('handlerid')
entry.handler_block(_hid)
_pos = entry.get_position()
@@ -48,6 +48,7 @@ class DBRestore(object):
new_text = ""
_pos = entry.insert_text(new_text, _pos)
entry.handler_unblock(_hid)
def _move_cursor():
with gtk.gdk.lock:
entry.set_position(_pos)
@@ -158,7 +159,8 @@ class DBRestore(object):
img_restore.set_from_stock('tryton-folder-saved-search', \
gtk.ICON_SIZE_BUTTON)
self.button_restore.set_image(img_restore)
self.tooltips.set_tip(self.button_restore, _('Restore the database from file.'))
self.tooltips.set_tip(self.button_restore,
_('Restore the database from file.'))
self.dialog.add_action_widget(self.button_restore, gtk.RESPONSE_OK)
self.dialog.set_default_response(gtk.RESPONSE_OK)
+3 -4
View File
@@ -14,9 +14,9 @@ class Email(object):
def __init__(self):
self.parent = get_toplevel_window()
self.win = gtk.Dialog(_('Email'), self.parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
gtk.RESPONSE_OK))
self.win.set_default_response(gtk.RESPONSE_OK)
self.win.set_icon(TRYTON_ICON)
self.win.set_has_separator(True)
@@ -84,7 +84,6 @@ class Email(object):
self.win.vbox.pack_start(hbox, expand=True, fill=True)
self.win.show_all()
def run(self):
-88
View File
@@ -1,88 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"File actions"
import gtk
import gettext
from tryton.config import TRYTON_ICON, CONFIG
from tryton.common import safe_eval, get_toplevel_window
_ = gettext.gettext
class FileActions(object):
"File actions window"
def __init__(self):
self.parent= get_toplevel_window()
self.win = gtk.Dialog(_('File Actions'), self.parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
self.win.set_default_response(gtk.RESPONSE_OK)
self.win.set_icon(TRYTON_ICON)
self.win.set_has_separator(True)
self.win.vbox.pack_start(gtk.Label(
_('Edit Files Actions')), expand=False, fill=True)
self.win.vbox.pack_start(gtk.HSeparator())
self.treeview = gtk.TreeView()
self.model = gtk.ListStore(str, str, str, str)
self.treeview.set_model(self.model)
for index, text in enumerate((_('File Type'), _('Open'), _('Print'))):
renderer = gtk.CellRendererText()
if index != 0:
renderer.set_property('editable', True)
renderer.connect('edited', self._sig_edited)
column = gtk.TreeViewColumn(text, renderer, text=index + 1)
column.set_resizable(True)
self.treeview.append_column(column)
i = 1
if isinstance(CONFIG['client.actions'], basestring):
CONFIG['client.actions'] = safe_eval(CONFIG['client.actions'])
extensions = CONFIG['client.actions'].keys()
extensions.sort()
for extension in extensions:
iter = self.model.append()
self.model.set(iter, 0, extension,
1, _('%s file') % extension.upper(),
2, CONFIG['client.actions'][extension][0],
3, CONFIG['client.actions'][extension][1])
scroll = gtk.ScrolledWindow()
scroll.add(self.treeview)
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
viewport= gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_ETCHED_IN)
viewport.set_size_request(400, 200)
viewport.add(scroll)
self.win.vbox.pack_start(viewport, expand=True, fill=True)
label = gtk.Label(_('Use "%s" as a placeholder for the file name'))
label.set_alignment(0.0, 0.5)
label.set_padding(0, 10)
self.win.vbox.pack_start(label, expand=False, fill=True)
self.win.show_all()
def run(self):
"Run the window"
res = self.win.run()
if res == gtk.RESPONSE_OK:
config = {}
for extension, _, cmd_open, cmd_print in self.model:
config[extension] = {
0: cmd_open,
1: cmd_print,
}
CONFIG['client.actions'] = config
CONFIG.save()
self.parent.present()
self.win.destroy()
return res
def _sig_edited(self, cell, path, new_text):
iter = self.model.get_iter_from_string(path)
(path, column) = self.treeview.get_cursor()
column_id = self.treeview.get_columns().index(column)
self.model.set(iter, column_id + 1, new_text)
+10 -8
View File
@@ -8,10 +8,8 @@ import locale
import tryton.rpc as rpc
from tryton.gui.window.view_form.screen import Screen
from tryton.action import Action
from tryton.config import CONFIG
from tryton.gui import Main
from tryton.gui.window import Window
from tryton.gui.window.preference import Preference
from tryton.gui.window.win_export import WinExport
from tryton.gui.window.win_import import WinImport
from tryton.gui.window.attachment import Attachment
@@ -21,7 +19,7 @@ from tryton.common import (TRYTON_ICON, message, sur, sur_3b, COLOR_SCHEMES,
from tryton.exceptions import TrytonServerError
import tryton.common as common
from tryton.translate import date_format
from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT
from tryton.common import HM_FORMAT
from tryton.pyson import PYSONEncoder
from tabcontent import TabContent
@@ -183,9 +181,9 @@ class Form(SignalEvent, TabContent):
return
parent = common.get_toplevel_window()
win = gtk.Dialog(_('Go to ID'), parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
gtk.RESPONSE_OK))
win.set_icon(TRYTON_ICON)
win.set_has_separator(True)
win.set_default_response(gtk.RESPONSE_OK)
@@ -200,6 +198,7 @@ class Form(SignalEvent, TabContent):
entry.set_property('activates_default', True)
entry.set_max_length(0)
entry.set_alignment(1.0)
def sig_insert_text(widget, new_text, new_text_length, position):
value = widget.get_text()
position = widget.get_position()
@@ -240,7 +239,9 @@ class Form(SignalEvent, TabContent):
def sig_attach(self, widget=None):
obj_id = self.id_get()
if obj_id >= 0 and obj_id is not False:
Attachment(self.model, obj_id).run()
def callback():
self.update_attachment_count(reload=True)
Attachment(self.model, obj_id, callback)
else:
self.message_info(_('No record selected!'))
self.update_attachment_count(reload=True)
@@ -300,7 +301,7 @@ class Form(SignalEvent, TabContent):
display_format = date_format() + ' ' + HM_FORMAT
date = timezoned_date(line[key])
value = common.datetime_strftime(date, display_format)
message_str += val + ' ' + value +'\n'
message_str += val + ' ' + value + '\n'
message_str += _('Model:') + ' ' + self.model
message(message_str)
return True
@@ -482,6 +483,7 @@ class Form(SignalEvent, TabContent):
self.activate_save()
def modified_save(self, reload=True):
self.screen.current_view.set_value()
if self.screen.modified():
value = sur_3b(
_('This record has been modified\n' \
+6 -5
View File
@@ -15,9 +15,9 @@ class Limit(object):
def __init__(self):
self.parent = get_toplevel_window()
self.win = gtk.Dialog(_('Limit'), self.parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
gtk.RESPONSE_OK))
self.win.set_default_response(gtk.RESPONSE_OK)
self.win.set_icon(TRYTON_ICON)
self.win.set_has_separator(True)
@@ -29,8 +29,9 @@ class Limit(object):
hbox.pack_start(gtk.Label(_('Limit:')), expand=True, fill=True)
self.spin_limit = gtk.SpinButton(climb_rate=1, digits=0)
self.spin_limit.set_numeric(False)
self.spin_limit.set_adjustment(gtk.Adjustment(value=CONFIG['client.limit'],
lower=1, upper=sys.maxint, step_incr=10, page_incr=100))
self.spin_limit.set_adjustment(gtk.Adjustment(
value=CONFIG['client.limit'], lower=1, upper=sys.maxint,
step_incr=10, page_incr=100))
hbox.pack_start(self.spin_limit, expand=True, fill=True)
self.win.vbox.pack_start(hbox, expand=True, fill=True)
+1 -1
View File
@@ -19,7 +19,7 @@ class Preference(object):
def __init__(self, user):
self.parent = common.get_toplevel_window()
self.win = gtk.Dialog(_('Preferences'), self.parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
self.win.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
self.win.set_has_separator(False)
self.win.set_icon(TRYTON_ICON)
+1 -1
View File
@@ -91,7 +91,7 @@ Examples:
label.set_alignment(0, 0.5)
table.attach(label, 1, 2, i, i + 1,
yoptions=False, xoptions=gtk.FILL)
i +=1
i += 1
return table
def run(self):
+2 -4
View File
@@ -18,7 +18,8 @@ class Tips(object):
'''),
_(u'''<b>Do you know Triton, one of the namesakes for our project?</b>
_(
u'''<b>Do you know Triton, one of the namesakes for our project?</b>
Triton (pronounced /ˈtraɪtən/ TRYE-tən, or as in Greek Τρίτων) is the
largest moon of the planet Neptune, discovered on October 10, 1846
@@ -41,7 +42,6 @@ You can save any graphs in PNG file with right-click on it.
'''),
]
parent = get_toplevel_window()
self.win = gtk.Dialog(_('Tips'), parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
@@ -118,5 +118,3 @@ You can save any graphs in PNG file with right-click on it.
def tip_previous(self, widget):
self.number -= 1
self.tip_set()
+3 -7
View File
@@ -4,14 +4,9 @@
import gtk
from tryton.gui.window.view_form.screen import Screen
import tryton.rpc as rpc
import time
import datetime
from tryton.gui.window.win_search import WinSearch
from tryton.action import Action as Action2
import tryton.common as common
from tryton.pyson import PYSONDecoder
import gettext
from tryton.config import CONFIG
from tryton.signal_event import SignalEvent
from tryton.exceptions import TrytonServerError
from tryton.gui.window.win_form import WinForm
@@ -89,7 +84,7 @@ class Action(SignalEvent):
else:
self.title.set_text(name)
self.widget.set_size_request(int(attrs.get('width',-1)),
self.widget.set_size_request(int(attrs.get('width', -1)),
int(attrs.get('height', -1)))
self.screen.search_filter()
@@ -97,6 +92,7 @@ class Action(SignalEvent):
def row_activate(self):
if not self.screen.current_record:
pass
def callback(result):
if result:
self.screen.current_record.save()
@@ -133,5 +129,5 @@ class Action(SignalEvent):
return
del self.domain[:]
self.domain.extend(new_domain)
if hasattr(self, 'screen'): # Catch early update
if hasattr(self, 'screen'): # Catch early update
self.display()
+16 -32
View File
@@ -2,10 +2,13 @@
#this repository contains the full copyright notices and license terms.
'Parser'
import gtk
from tryton.gui.window.view_form.view.form_gtk.parser import _container
import gettext
from tryton.gui.window.view_form.view.form_gtk.parser import _container, VBox
import tryton.common as common
from action import Action
from tryton.config import CONFIG, TRYTON_ICON
from tryton.config import CONFIG
_ = gettext.gettext
class ParserBoard(object):
@@ -47,13 +50,6 @@ class ParserBoard(object):
xexpand=xexpand, xfill=xfill)
elif node.localName == 'separator':
text = attrs.get('string', '')
if 'string' in attrs or 'name' in attrs:
if not text:
if 'name' in attrs and attrs['name'] in fields:
if 'states' in fields[attrs['name']]:
attrs['states'] = \
fields[attrs['name']]['states']
text = fields[attrs['name']]['string']
vbox = VBox(attrs=attrs)
if text:
label = gtk.Label(text)
@@ -68,22 +64,6 @@ class ParserBoard(object):
xexpand=xexpand, xfill=xfill)
elif node.localName == 'label':
text = attrs.get('string', '')
if not text:
if 'name' in attrs and attrs['name'] in fields:
if 'states' in fields[attrs['name']]:
attrs['states'] = fields[attrs['name']]['states']
if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
text = _(':') + fields[attrs['name']]['string']
else:
text = fields[attrs['name']]['string'] + _(':')
if 'align' not in attrs:
attrs['align'] = 1.0
else:
for node in node.childNodes:
if node.nodeType == node.TEXT_NODE:
text += node.data
else:
text += node.toxml()
if not text:
container.empty_add(int(attrs.get('colspan', 1)))
continue
@@ -115,7 +95,8 @@ class ParserBoard(object):
container.wid_add(notebook,
colspan=int(attrs.get('colspan', 4)),
yexpand=True, yfill=True)
widget, new_widgets = self.parse(node, notebook, tooltips=tooltips)
widget, new_widgets = self.parse(node, notebook,
tooltips=tooltips)
widgets += new_widgets
elif node.localName == 'page':
if CONFIG['client.form_tab'] == 'left':
@@ -124,9 +105,10 @@ class ParserBoard(object):
angle = -90
else:
angle = 0
label = gtk.Label(attrs.get('string','No String Attr.'))
label = gtk.Label(attrs.get('string', _('No String Attr.')))
label.set_angle(angle)
widget, new_widgets = self.parse(node, notebook, tooltips=tooltips)
widget, new_widgets = self.parse(node, notebook,
tooltips=tooltips)
widgets += new_widgets
notebook.append_page(widget, label)
elif node.localName == 'group':
@@ -145,7 +127,8 @@ class ParserBoard(object):
hpaned = gtk.HPaned()
container.wid_add(hpaned, colspan=int(attrs.get('colspan', 4)),
yexpand=True, yfill=True)
widget, new_widgets = self.parse(node, paned=hpaned, tooltips=tooltips)
widget, new_widgets = self.parse(node, paned=hpaned,
tooltips=tooltips)
widgets += new_widgets
if 'position' in attrs:
hpaned.set_position(int(attrs['position']))
@@ -153,19 +136,20 @@ class ParserBoard(object):
vpaned = gtk.VPaned()
container.wid_add(vpaned, colspan=int(attrs.get('colspan', 4)),
yexpand=True, yfill=True)
widget, new_widgets = self.parse(node, paned=vpaned, tooltips=tooltips)
widget, new_widgets = self.parse(node, paned=vpaned,
tooltips=tooltips)
widgets += new_widgets
if 'position' in attrs:
vpaned.set_position(int(attrs['position']))
elif node.localName == 'child':
widget, new_widgets = self.parse(node, paned=paned, tooltips=tooltips)
widget, new_widgets = self.parse(node, paned=paned,
tooltips=tooltips)
widgets += new_widgets
if not paned.get_child1():
paned.pack1(widget, resize=True, shrink=True)
elif not paned.get_child2():
paned.pack2(widget, resize=True, shrink=True)
elif node.localName == 'action':
name = str(attrs['name'])
widget_act = Action(attrs, self.context)
widgets.append(widget_act)
yexpand = bool(attrs.get('yexpand', 1))
@@ -6,6 +6,7 @@ import xml.dom.minidom
from parser import ParserBoard
from tryton.gui.window.view_board.action import Action
class ViewBoard(object):
'View board'
+41 -38
View File
@@ -3,14 +3,13 @@
import os
import tempfile
import tryton.rpc as rpc
from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT, datetime_strftime, \
from tryton.common import DT_FORMAT, DHM_FORMAT, datetime_strftime, \
domain_inversion, eval_domain, localize_domain, unlocalize_domain, \
merge, inverse_leaf, EvalEnvironment, RPCProgress
import tryton.common as common
import time
import datetime
from decimal import Decimal
import logging
from tryton.exceptions import TrytonServerError
@@ -60,8 +59,8 @@ class CharField(object):
def validation_domains(self, record):
screen_domain, attr_domain = self.domains_get(record)
if attr_domain:
return screen_domain, screen_domain + unlocalize_domain(attr_domain,
self.name)
return (screen_domain, screen_domain +
unlocalize_domain(attr_domain, self.name))
else:
return screen_domain, screen_domain
@@ -91,12 +90,13 @@ class CharField(object):
elif domain == [('id', '=', False)]:
res = False
else:
if (isinstance(inverted_domain, list) \
and len(inverted_domain) == 1 and inverted_domain[0][1] == '='):
# If the inverted domain is so constraint that only one value is
# possible we should use it. But we must also pay attention to
# the fact that the original domain might be a 'OR' domain and
# thus not preventing the modification of fields.
if (isinstance(inverted_domain, list)
and len(inverted_domain) == 1
and inverted_domain[0][1] == '='):
# If the inverted domain is so constraint that only one value
# is possible we should use it. But we must also pay attention
# to the fact that the original domain might be a 'OR' domain
# and thus not preventing the modification of fields.
leftpart, _, value = inverted_domain[0][:3]
setdefault = True
if '.' in leftpart:
@@ -135,9 +135,9 @@ class CharField(object):
def set_client(self, record, value, force_change=False):
internal = record.value.get(self.name, False)
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
if (internal or False) != (record.value.get(self.name, False) or False):
if ((internal or False)
!= (record.value.get(self.name, False) or False)):
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
self.sig_changed(record)
@@ -230,11 +230,10 @@ class FloatField(CharField):
def set_client(self, record, value, force_change=False):
internal = record.value.get(self.name)
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
digits = record.expr_eval(self.attrs.get('digits', (16, 2)))
if abs(float(internal or 0.0) - float(record.value[self.name] or 0.0)) \
>= (10.0**(-int(digits[1]))):
if (abs(float(internal or 0.0) - float(record.value[self.name] or 0.0))
>= (10.0 ** (-int(digits[1])))):
if not self.get_state_attrs(record).get('readonly', False):
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
@@ -248,12 +247,11 @@ class NumericField(CharField):
def set_client(self, record, value, force_change=False):
value = Decimal(str(value))
internal = record.value.get(self.name)
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
digits = record.expr_eval(self.attrs.get('digits', (16, 2)))
if abs((internal or Decimal('0.0')) - \
(record.value[self.name] or Decimal('0.0'))) \
>= Decimal(str(10.0**(-int(digits[1])))):
if (abs((internal or Decimal('0.0'))
- (record.value[self.name] or Decimal('0.0')))
>= Decimal(str(10.0 ** (-int(digits[1]))))):
if not self.get_state_attrs(record).get('readonly', False):
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
@@ -270,12 +268,12 @@ class IntegerField(CharField):
def get_client(self, record):
return record.value.get(self.name) or 0
class BooleanField(CharField):
def set_client(self, record, value, force_change=False):
value = bool(value)
internal = bool(record.value.get(self.name, False))
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
if internal != bool(record.value.get(self.name, False)):
record.modified_fields.setdefault(self.name)
@@ -367,7 +365,6 @@ class M2OField(CharField):
def set_client(self, record, value, force_change=False):
internal = record.value.get(self.name) or (False, '')
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
if (internal[0] or False) != (record.value[self.name][0] or False):
record.modified_fields.setdefault(self.name)
@@ -394,7 +391,8 @@ class M2OField(CharField):
def domain_get(self, record):
screen_domain, attr_domain = self.domains_get(record)
return localize_domain(inverse_leaf(screen_domain), self.name) + attr_domain
return (localize_domain(inverse_leaf(screen_domain), self.name)
+ attr_domain)
def get_state_attrs(self, record):
result = super(M2OField, self).get_state_attrs(record)
@@ -404,6 +402,7 @@ class M2OField(CharField):
result['readonly'] = True
return result
class O2OField(M2OField):
pass
@@ -453,7 +452,8 @@ class O2MField(CharField):
if record.model_name == self.attrs['relation']:
group.fields = record.group.fields
group.signal_connect(group, 'group-changed', self._group_changed)
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
group.signal_connect(group, 'group-list-changed',
self._group_list_changed)
group.signal_connect(group, 'group-cleared', self._group_cleared)
record.value[self.name] = group
@@ -493,10 +493,10 @@ class O2MField(CharField):
if record.value.get(self.name) is None:
return {}
result = {}
for record2 in (record.value[self.name] \
+ record.value[self.name].record_removed \
for record2 in (record.value[self.name]
+ record.value[self.name].record_removed
+ record.value[self.name].record_deleted):
result.update(record2.get_timestamp())
result.update(record2.get_timestamp())
return result
def get_eval(self, record, check_load=True):
@@ -530,7 +530,8 @@ class O2MField(CharField):
record.value[self.name] = group
group.load(value, display=False)
group.signal_connect(group, 'group-changed', self._group_changed)
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
group.signal_connect(group, 'group-list-changed',
self._group_list_changed)
group.signal_connect(group, 'group-cleared', self._group_cleared)
if modified:
record.modified_fields.setdefault(self.name)
@@ -591,7 +592,8 @@ class O2MField(CharField):
new_record.set_default(vals, modified=modified)
group.add(new_record)
group.signal_connect(group, 'group-changed', self._group_changed)
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
group.signal_connect(group, 'group-list-changed',
self._group_list_changed)
group.signal_connect(group, 'group-cleared', self._group_cleared)
return True
@@ -717,7 +719,8 @@ class M2MField(O2MField):
group.fields = fields
group.load(value, display=False)
group.signal_connect(group, 'group-changed', self._group_changed)
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
group.signal_connect(group, 'group-list-changed',
self._group_list_changed)
group.signal_connect(group, 'group-cleared', self._group_cleared)
if modified:
record.modified_fields.setdefault(self.name)
@@ -744,7 +747,6 @@ class ReferenceField(CharField):
def set_client(self, record, value, force_change=False):
internal = record.value.get(self.name)
prev_modified_fields = record.modified_fields.copy()
self.set(record, value)
if (internal or False) != (record.value[self.name] or False):
record.modified_fields.setdefault(self.name)
@@ -792,6 +794,7 @@ class ReferenceField(CharField):
record.modified_fields.setdefault(self.name)
record.signal('record-modified')
class BinaryField(CharField):
def get(self, record, check_load=True, readonly=True, modified=False):
@@ -849,14 +852,14 @@ TYPES = {
'char': CharField,
'sha': CharField,
'float_time': FloatField,
'integer' : IntegerField,
'biginteger' : IntegerField,
'float' : FloatField,
'numeric' : NumericField,
'many2one' : M2OField,
'many2many' : M2MField,
'one2many' : O2MField,
'reference' : ReferenceField,
'integer': IntegerField,
'biginteger': IntegerField,
'float': FloatField,
'numeric': NumericField,
'many2one': M2OField,
'many2many': M2MField,
'one2many': O2MField,
'reference': ReferenceField,
'selection': SelectionField,
'boolean': BooleanField,
'datetime': DateTimeField,
+6 -5
View File
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import copy
import tryton.rpc as rpc
from record import Record
from field import Field, O2MField
@@ -236,8 +235,8 @@ class Group(SignalEvent, list):
ctx = rpc.CONTEXT.copy()
ctx.update(self._context)
if self.parent_datetime_field:
ctx['_datetime'] = self.parent.get_eval(check_load=False)\
[self.parent_datetime_field]
ctx['_datetime'] = self.parent.get_eval(check_load=False
)[self.parent_datetime_field]
return ctx
context = property(_get_context)
@@ -247,7 +246,8 @@ class Group(SignalEvent, list):
record.signal_unconnect(record.group)
record.group = self
record.signal_connect(self, 'record-changed', self._record_changed)
record.signal_connect(self, 'record-modified', self._record_modified)
record.signal_connect(self, 'record-modified',
self._record_modified)
if position == -1:
self.append(record)
else:
@@ -373,7 +373,8 @@ class Group(SignalEvent, list):
if len(new) and len(to_add):
ctx.update(self.context)
args = ('model', self.model_name, 'default_get', to_add.keys(), ctx)
args = ('model', self.model_name, 'default_get', to_add.keys(),
ctx)
try:
values = rpc.execute(*args)
except TrytonServerError, exception:
+25 -22
View File
@@ -4,10 +4,7 @@ import tryton.rpc as rpc
from tryton.signal_event import SignalEvent
import tryton.common as common
from tryton.pyson import PYSONDecoder
import field
import datetime
import logging
import time
import field as fields
from functools import reduce
from tryton.exceptions import TrytonServerError
@@ -33,14 +30,14 @@ class Record(SignalEvent):
self.modified_fields = {}
self._timestamp = None
self.attachment_count = -1
self.next = {} # Used in Group list
self.next = {} # Used in Group list
self.value = {}
self.autocompletion = {}
self.exception = False
def __getitem__(self, name, raise_exception=False):
if name not in self._loaded and self.id >= 0:
ids = [self.id]
ids = [self.id]
if name == '*':
loading = reduce(
lambda x, y: 'eager' if x == y == 'eager' else 'lazy',
@@ -65,21 +62,22 @@ class Record(SignalEvent):
ids.append(record.id)
n += 1
if loading == 'eager':
fields = [fname for fname, field in self.group.fields.iteritems()
if field.attrs.get('loading', 'eager') == 'eager']
fnames = [fname
for fname, field in self.group.fields.iteritems()
if field.attrs.get('loading', 'eager') == 'eager']
else:
fields = self.group.fields.keys()
fields = [fname for fname in fields if fname not in self._loaded]
fields.extend(('%s.rec_name' % fname for fname in fields[:]
fnames = self.group.fields.keys()
fnames = [fname for fname in fnames if fname not in self._loaded]
fnames.extend(('%s.rec_name' % fname for fname in fnames[:]
if self.group.fields[fname].attrs['type']
in ('many2one', 'one2one', 'reference')))
fields.append('_timestamp')
fnames.append('_timestamp')
ctx = rpc.CONTEXT.copy()
ctx.update(self.context_get())
ctx.update(dict(('%s.%s' % (self.model_name, fname), 'size')
for fname, field in self.group.fields.iteritems()
if field.attrs['type'] == 'binary'))
args = ('model', self.model_name, 'read', ids, fields, ctx)
args = ('model', self.model_name, 'read', ids, fnames, ctx)
exception = None
try:
values = rpc.execute(*args)
@@ -89,7 +87,7 @@ class Record(SignalEvent):
values = common.process_exception(exception, *args)
if not values:
values = [{'id': x} for x in ids]
default_values = dict((f, False) for f in fields)
default_values = dict((f, False) for f in fnames)
for value in values:
value.update(default_values)
self.exception = True
@@ -391,10 +389,11 @@ class Record(SignalEvent):
for fieldname, value in val.items():
if fieldname not in self.group.fields:
continue
if isinstance(self.group.fields[fieldname], field.M2OField):
if isinstance(self.group.fields[fieldname], fields.M2OField):
if fieldname + '.rec_name' in val:
value = (value, val[fieldname + '.rec_name'])
elif isinstance(self.group.fields[fieldname], field.ReferenceField):
elif isinstance(self.group.fields[fieldname],
fields.ReferenceField):
if value:
ref_model, ref_id = value.split(',', 1)
if fieldname + '.rec_name' in val:
@@ -417,13 +416,14 @@ class Record(SignalEvent):
continue
if fieldname not in self.group.fields:
continue
if isinstance(self.group.fields[fieldname], field.O2MField):
if isinstance(self.group.fields[fieldname], fields.O2MField):
later[fieldname] = value
continue
if isinstance(self.group.fields[fieldname], field.M2OField):
if isinstance(self.group.fields[fieldname], fields.M2OField):
if fieldname + '.rec_name' in val:
value = (value, val[fieldname + '.rec_name'])
elif isinstance(self.group.fields[fieldname], field.ReferenceField):
elif isinstance(self.group.fields[fieldname],
fields.ReferenceField):
if value:
ref_model, ref_id = value.split(',', 1)
if fieldname + '.rec_name' in val:
@@ -501,14 +501,14 @@ class Record(SignalEvent):
for fieldname, value in res.items():
if fieldname not in self.group.fields:
continue
if isinstance(self.group.fields[fieldname], field.O2MField):
if isinstance(self.group.fields[fieldname], fields.O2MField):
later[fieldname] = value
continue
if isinstance(self.group.fields[fieldname], field.M2OField):
if isinstance(self.group.fields[fieldname], fields.M2OField):
if fieldname + '.rec_name' in res:
value = (value, res[fieldname + '.rec_name'])
elif isinstance(self.group.fields[fieldname],
field.ReferenceField):
fields.ReferenceField):
if value:
ref_model, ref_id = value.split(',', 1)
if fieldname + '.rec_name' in res:
@@ -602,5 +602,8 @@ class Record(SignalEvent):
def destroy(self):
super(Record, self).destroy()
self.group = None
for v in self.value.itervalues():
if hasattr(v, 'destroy'):
v.destroy()
self.value = None
self.next = None
+10 -8
View File
@@ -6,7 +6,6 @@ try:
import simplejson as json
except ImportError:
import json
import copy
import collections
import xml.dom.minidom
import tryton.rpc as rpc
@@ -14,7 +13,6 @@ from tryton.gui.window.view_form.model.group import Group
from tryton.gui.window.view_form.model.record import Record
from tryton.gui.window.view_form.view.screen_container import ScreenContainer
from tryton.signal_event import SignalEvent
from tryton.common import node_attributes
from tryton.config import CONFIG
import tryton.common as common
from tryton.exceptions import TrytonServerError, TrytonServerUnavailable
@@ -368,13 +366,18 @@ class Screen(SignalEvent):
self.fields_view_tree = view
break
# Ensure that loading is always eager for fields on tree view
# and always lazy for fields only on form view
if node.localName == 'tree':
loading = 'eager'
else:
loading = 'lazy'
for field in fields:
if field not in self.group.fields:
if field not in self.group.fields or loading == 'eager':
fields[field]['loading'] = loading
else:
fields[field]['loading'] = \
self.group.fields[field].attrs['loading']
children_field = view.get('field_childs')
@@ -436,9 +439,6 @@ class Screen(SignalEvent):
record = group.new(default, self.domain, ctx)
group.add(record, self.new_model_position())
self.current_record = record
fields = None
if self.current_view:
fields = self.current_view.get_fields()
self.display()
self.set_cursor(new=True)
self.request_set()
@@ -505,7 +505,8 @@ class Screen(SignalEvent):
elif current_view.view_type == 'tree' and not self.current_record:
# The widget might have been destroyed
if self.screen_container:
self.screen_container.set_cursor(new=new, reset_view=reset_view)
self.screen_container.set_cursor(new=new,
reset_view=reset_view)
elif current_view.view_type in ('tree', 'form'):
current_view.set_cursor(new=new, reset_view=reset_view)
@@ -528,7 +529,8 @@ class Screen(SignalEvent):
res = False
if self.current_view.view_type != 'tree':
if self.current_record:
res = self.current_record.modified or self.current_record.id < 0
res = (self.current_record.modified
or self.current_record.id < 0)
else:
for record in self.group:
if record.modified or record.id < 0:
-3
View File
@@ -4,11 +4,9 @@ import operator
from functools import reduce
import gtk
import gettext
from tryton.common import message, TRYTON_ICON
import tryton.rpc as rpc
import tryton.common as common
from interface import ParserView
from tryton.config import CONFIG
from tryton.exceptions import TrytonServerError
from tryton.action import Action
@@ -196,4 +194,3 @@ class ViewForm(ParserView):
self.screen.reload(written=True)
else:
self.screen.display()
@@ -5,7 +5,7 @@ import gettext
import os
import tempfile
from tryton.common import common
from tryton.common import file_selection, message, warning, Tooltips, file_open
from tryton.common import file_selection, Tooltips, file_open
from interface import WidgetInterface
_ = gettext.gettext
@@ -24,12 +24,15 @@ class Binary(WidgetInterface):
self.widget = gtk.HBox(spacing=0)
self.wid_size = gtk.Entry()
self.wid_size.set_width_chars(10)
self.wid_size.set_alignment(1.0)
self.wid_size.props.sensitive = False
if self.filename and attrs.get('filename_visible'):
self.wid_text = gtk.Entry()
self.wid_text.set_property('activates_default', True)
self.wid_text.connect('focus-in-event', lambda x, y: self._focus_in())
self.wid_text.connect('focus-out-event', lambda x, y: self._focus_out())
self.wid_text.connect('focus-in-event',
lambda x, y: self._focus_in())
self.wid_text.connect('focus-out-event',
lambda x, y: self._focus_out())
self.wid_text.connect_after('key_press_event', self.sig_key_press)
self.widget.pack_start(self.wid_text, expand=True, fill=True)
else:
@@ -136,7 +139,7 @@ class Binary(WidgetInterface):
filename = file_selection(_('Save As...'), filename=filename,
action=gtk.FILE_CHOOSER_ACTION_SAVE)
if filename:
with open(filename,'wb') as fp:
with open(filename, 'wb') as fp:
fp.write(self.field.get_data(self.record))
def sig_remove(self, widget=None):
@@ -4,10 +4,8 @@ import time
import datetime
import gtk
import gettext
import locale
from interface import WidgetInterface
import tryton.rpc as rpc
from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT, message, \
from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT, \
TRYTON_ICON, timezoned_date
from tryton.common import date_widget, Tooltips, datetime_strftime, \
get_toplevel_window
@@ -89,7 +87,7 @@ class Calendar(WidgetInterface):
if not value:
self.entry.clear()
else:
if len(value)>10:
if len(value) > 10:
value = value[:10]
date = datetime.date(*time.strptime(value, DT_FORMAT)[:3])
value = datetime_strftime(date, self.format)
@@ -101,9 +99,9 @@ class Calendar(WidgetInterface):
def cal_open(self, widget):
parent = get_toplevel_window()
win = gtk.Dialog(_('Date Selection'), parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
gtk.RESPONSE_OK))
win.set_has_separator(True)
win.set_icon(TRYTON_ICON)
@@ -120,7 +118,7 @@ class Calendar(WidgetInterface):
try:
val = self.get_value(self.record)
if val:
cal.select_month(int(val[5:7])-1, int(val[0:4]))
cal.select_month(int(val[5:7]) - 1, int(val[0:4]))
cal.select_day(int(val[8:10]))
except ValueError:
pass
@@ -220,9 +218,9 @@ class DateTime(WidgetInterface):
def cal_open(self, widget):
parent = get_toplevel_window()
win = gtk.Dialog(_('Date Time Selection'), parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
gtk.RESPONSE_OK))
win.set_has_separator(True)
win.set_icon(TRYTON_ICON)
win.vbox.set_spacing(2)
@@ -252,7 +250,7 @@ class DateTime(WidgetInterface):
if val:
widget_hour.set_value(int(val[11:13]))
widget_minute.set_value(int(val[-5:-3]))
cal.select_month(int(val[5:7])-1, int(val[0:4]))
cal.select_month(int(val[5:7]) - 1, int(val[0:4]))
cal.select_day(int(val[8:10]))
else:
widget_hour.set_value(time.localtime()[3])
@@ -6,6 +6,7 @@ import gettext
_ = gettext.gettext
class CheckBox(WidgetInterface):
def __init__(self, field_name, model_name, attrs=None):
@@ -67,4 +67,3 @@ class Float(Integer):
if len(new_int) > digits[0] \
or len(new_decimal) > digits[1]:
entry.stop_emission('insert-text')
@@ -35,8 +35,9 @@ class FloatTime(WidgetInterface):
value = self.entry.get_text()
if not value:
return field.set_client(record, 0.0)
digits = record.expr_eval(field.attrs.get('digits', (16, 2)))
return field.set_client(record,
common.text_to_float_time(value, self.conv))
round(common.text_to_float_time(value, self.conv), digits[1]))
def display(self, record, field):
super(FloatTime, self).display(record, field)
@@ -54,7 +54,8 @@ class Image(WidgetInterface):
if self.filename:
self.but_open = gtk.Button()
img_open = gtk.Image()
img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_open.set_from_stock('tryton-open',
gtk.ICON_SIZE_SMALL_TOOLBAR)
self.but_open.set_image(img_open)
self.but_open.set_relief(gtk.RELIEF_NONE)
self.but_open.connect('clicked', self.sig_open)
@@ -65,7 +66,8 @@ class Image(WidgetInterface):
self.but_save_as = gtk.Button()
img_save_as = gtk.Image()
img_save_as.set_from_stock('tryton-save', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_save_as.set_from_stock('tryton-save',
gtk.ICON_SIZE_SMALL_TOOLBAR)
self.but_save_as.set_image(img_save_as)
self.but_save_as.set_relief(gtk.RELIEF_NONE)
self.but_save_as.connect('clicked', self.sig_save_as)
@@ -74,7 +76,8 @@ class Image(WidgetInterface):
self.but_remove = gtk.Button()
img_remove = gtk.Image()
img_remove.set_from_stock('tryton-clear', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_remove.set_from_stock('tryton-clear',
gtk.ICON_SIZE_SMALL_TOOLBAR)
self.but_remove.set_image(img_remove)
self.but_remove.set_relief(gtk.RELIEF_NONE)
self.but_remove.connect('clicked', self.sig_remove)
@@ -193,7 +196,7 @@ class Image(WidgetInterface):
if self.field:
value = self.field.get_client(self.record)
if isinstance(value, (int, long)):
if value > 10**6:
if value > 10 ** 6:
value = False
else:
value = self.field.get_data(self.record)
@@ -9,7 +9,7 @@ class WidgetInterface(object):
def __init__(self, field_name, model_name, attrs=None):
self.field_name = field_name
self.model_name = model_name
self.view = None # Filled by ViewForm
self.view = None # Filled by ViewForm
self.attrs = attrs or {}
for attr_name in ('readonly', 'invisible'):
if attr_name in self.attrs:
@@ -50,7 +50,6 @@ class Many2Many(WidgetInterface):
self.but_add.set_relief(gtk.RELIEF_NONE)
hbox.pack_start(self.but_add, expand=False, fill=False)
self.but_remove = gtk.Button()
tooltips.set_tip(self.but_remove, _('Remove'))
self.but_remove.connect('clicked', self._sig_remove)
@@ -130,7 +129,7 @@ class Many2Many(WidgetInterface):
else:
dom = domain
ids = rpc.execute('model', self.attrs['relation'], 'search',
dom , 0, CONFIG['client.limit'], None, context)
dom, 0, CONFIG['client.limit'], None, context)
except TrytonServerError, exception:
common.process_exception(exception)
return False
@@ -1,10 +1,8 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gobject
import gtk
import gettext
from interface import WidgetInterface
from tryton.common import TRYTON_ICON, COLOR_SCHEMES
import tryton.common as common
from tryton.gui.window.view_form.screen import Screen
from tryton.gui.window.win_search import WinSearch
@@ -14,7 +12,6 @@ from tryton.action import Action
from tryton.config import CONFIG
from tryton.pyson import PYSONEncoder
from tryton.exceptions import TrytonServerError
import pango
_ = gettext.gettext
@@ -33,7 +30,8 @@ class Many2One(WidgetInterface):
self.wid_text.connect_after('key_press_event', self.sig_key_press)
self.wid_text.connect('populate-popup', self._populate_popup)
self.wid_text.connect('focus-in-event', lambda x, y: self._focus_in())
self.wid_text.connect('focus-out-event', lambda x, y: self._focus_out())
self.wid_text.connect('focus-out-event',
lambda x, y: self._focus_out())
self.wid_text.connect_after('changed', self.sig_changed)
self.changed = True
self.wid_text.connect('activate', self.sig_activate)
@@ -118,12 +116,13 @@ class Many2One(WidgetInterface):
common.process_exception(exception)
self.changed = True
return
if len(ids)==1:
if len(ids) == 1:
self.field.set_client(self.record, ids[0],
force_change=True)
self.focus_out = True
self.display(self.record, self.field)
return
def callback(ids):
if ids:
self.field.set_client(self.record, ids[0],
@@ -150,6 +149,7 @@ class Many2One(WidgetInterface):
def sig_new(self, *args):
self.focus_out = False
screen = self.get_screen()
def callback(result):
if result and screen.save_current():
value = (screen.current_record.id,
@@ -165,6 +165,7 @@ class Many2One(WidgetInterface):
if value:
screen = self.get_screen()
screen.load([self.field.get(self.record)])
def callback(result):
if result and screen.save_current():
value = (screen.current_record.id,
@@ -200,7 +201,7 @@ class Many2One(WidgetInterface):
common.process_exception(exception)
self.changed = True
return False
if ids and len(ids)==1:
if ids and len(ids) == 1:
self.field.set_client(self.record, ids[0],
force_change=True)
self.focus_out = True
@@ -230,7 +231,8 @@ class Many2One(WidgetInterface):
elif event.keyval == gtk.keysyms.F2:
self.sig_edit(widget)
return True
elif event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return) and editable:
elif (event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return)
and editable):
self.sig_activate(widget, event, key_press=True)
return False
@@ -243,7 +245,7 @@ class Many2One(WidgetInterface):
return False
def set_value(self, record, field):
pass # No update of the model, the model is updated in real time !
pass # No update of the model, the model is updated in real time !
def display(self, record, field):
self.changed = False
@@ -280,9 +282,9 @@ class Many2One(WidgetInterface):
menu_entries.append((None, None, None))
menu_entries.append((None, None, None))
menu_entries.append((_('Actions'),
lambda x: self.click_and_action('form_action'),0))
lambda x: self.click_and_action('form_action'), 0))
menu_entries.append((_('Reports'),
lambda x: self.click_and_action('form_print'),0))
lambda x: self.click_and_action('form_print'), 0))
menu_entries.append((None, None, None))
for relate in relates:
relate['string'] = relate['name']
@@ -2,7 +2,6 @@
#this repository contains the full copyright notices and license terms.
import gtk
import gettext
from tryton.common import TRYTON_ICON, COLOR_SCHEMES
from interface import WidgetInterface
from tryton.gui.window.view_form.screen import Screen
from tryton.gui.window.win_search import WinSearch
@@ -11,7 +10,6 @@ from tryton.config import CONFIG
from tryton.exceptions import TrytonServerError
import tryton.common as common
import tryton.rpc as rpc
import pango
_ = gettext.gettext
@@ -47,7 +45,8 @@ class One2Many(WidgetInterface):
tooltips.set_tip(self.but_add, _('Add'))
self.but_add.connect('clicked', self._sig_add)
img_add = gtk.Image()
img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_add.set_from_stock('tryton-list-add',
gtk.ICON_SIZE_SMALL_TOOLBAR)
img_add.set_alignment(0.5, 0.5)
self.but_add.add(img_add)
self.but_add.set_relief(gtk.RELIEF_NONE)
@@ -57,7 +56,8 @@ class One2Many(WidgetInterface):
tooltips.set_tip(self.but_remove, _('Remove'))
self.but_remove.connect('clicked', self._sig_remove, True)
img_remove = gtk.Image()
img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_remove.set_from_stock('tryton-list-remove',
gtk.ICON_SIZE_SMALL_TOOLBAR)
img_remove.set_alignment(0.5, 0.5)
self.but_remove.add(img_remove)
self.but_remove.set_relief(gtk.RELIEF_NONE)
@@ -111,7 +111,8 @@ class One2Many(WidgetInterface):
tooltips.set_tip(self.but_pre, _('Previous'))
self.but_pre.connect('clicked', self._sig_previous)
img_pre = gtk.Image()
img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_pre.set_from_stock('tryton-go-previous',
gtk.ICON_SIZE_SMALL_TOOLBAR)
img_pre.set_alignment(0.5, 0.5)
self.but_pre.add(img_pre)
self.but_pre.set_relief(gtk.RELIEF_NONE)
@@ -136,7 +137,8 @@ class One2Many(WidgetInterface):
tooltips.set_tip(but_switch, _('Switch'))
but_switch.connect('clicked', self.switch_view)
img_switch = gtk.Image()
img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_switch.set_from_stock('tryton-fullscreen',
gtk.ICON_SIZE_SMALL_TOOLBAR)
img_switch.set_alignment(0.5, 0.5)
but_switch.add(img_switch)
but_switch.set_relief(gtk.RELIEF_NONE)
@@ -304,7 +306,8 @@ class One2Many(WidgetInterface):
try:
if self.wid_text.get_text():
dom = [('rec_name', 'ilike', '%' + self.wid_text.get_text() + '%'),
dom = [('rec_name', 'ilike',
'%' + self.wid_text.get_text() + '%'),
['OR', domain, ('id', 'in', removed_ids)]]
else:
dom = ['OR', domain, ('id', 'in', removed_ids)]
@@ -313,6 +316,7 @@ class One2Many(WidgetInterface):
except TrytonServerError, exception:
common.process_exception(exception)
return False
def callback(ids):
res_id = None
if ids:
@@ -328,7 +332,6 @@ class One2Many(WidgetInterface):
else:
callback(ids)
def _sig_label(self, screen, signal_data):
name = '_'
if signal_data[0] >= 1:
@@ -386,7 +389,7 @@ class One2Many(WidgetInterface):
def set_value(self, record, field):
self.screen.save_tree_state()
self.screen.current_view.set_value()
if self.screen.modified(): # TODO check if required
if self.screen.modified(): # TODO check if required
record.modified_fields.setdefault(field.name)
record.signal('record-modified')
return True
@@ -72,7 +72,7 @@ class Image(gtk.Image):
class Frame(gtk.Frame):
def __init__(self, label=None, attrs=None):
if not label: # label must be None to have no label widget
if not label: # label must be None to have no label widget
label = None
super(Frame, self).__init__(label=label)
self.attrs = attrs or {}
@@ -137,8 +137,8 @@ class _container(object):
table.set_col_spacings(0)
table.set_row_spacings(0)
table.set_border_width(0)
self.cont.append( (table, 0, 0) )
self.col.append( col )
self.cont.append((table, 0, 0))
self.col.append(col)
def get(self):
return self.cont[-1][0]
@@ -155,8 +155,8 @@ class _container(object):
table.resize(height + 1, self.col[-1])
def wid_add(self, widget, name='', yexpand=False, ypadding=2, rowspan=1,
colspan=1, translate=False, fname=None, help_tip=False, yfill=False,
xexpand=True, xfill=True, xpadding=3):
colspan=1, translate=False, fname=None, help_tip=False,
yfill=False, xexpand=True, xfill=True, xpadding=3):
(table, width, height) = self.cont[-1]
if colspan > self.col[-1]:
colspan = self.col[-1]
@@ -182,7 +182,8 @@ class _container(object):
img.set_from_stock('tryton-locale', gtk.ICON_SIZE_SMALL_TOOLBAR)
button.set_image(img)
button.set_relief(gtk.RELIEF_NONE)
self.trans_box.append((button, name, fname, widget.get_children()[0]))
self.trans_box.append((button, name, fname,
widget.get_children()[0]))
widget.get_child().pack_start(button, fill=False, expand=False)
widget.show_all()
table.attach(widget, width, width + colspan,
@@ -355,9 +356,9 @@ class ParserForm(ParserInterface):
colspan=colspan,
yexpand=yexpand, yfill=yfill,
xexpand=xexpand, xfill=xfill)
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
self.parse(model_name, node, fields, notebook,
tooltips=tooltips)
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
= self.parse(model_name, node, fields, notebook,
tooltips=tooltips)
if not cursor_widget:
cursor_widget = cursor_widget2
notebook_list.extend(notebook_list2)
@@ -369,7 +370,7 @@ class ParserForm(ParserInterface):
elif node.localName == 'page':
if CONFIG['client.form_tab'] == 'left':
angle = 90
tab_box = gtk.VBox(spacing=3)
tab_box = gtk.VBox(spacing=3)
image_pos, image_rotate = ('end',
gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)
elif CONFIG['client.form_tab'] == 'right':
@@ -412,9 +413,9 @@ class ParserForm(ParserInterface):
else:
tab_box.pack_start(icon)
tab_box.show_all()
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
self.parse(model_name, node, fields, notebook,
tooltips=tooltips)
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
= self.parse(model_name, node, fields, notebook,
tooltips=tooltips)
if not cursor_widget:
cursor_widget = cursor_widget2
notebook_list.extend(notebook_list2)
@@ -482,8 +483,8 @@ class ParserForm(ParserInterface):
xexpand=xexpand, xfill=xfill)
elif node.localName == 'group':
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
self.parse(model_name, node, fields, tooltips=tooltips)
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
= self.parse(model_name, node, fields, tooltips=tooltips)
if not cursor_widget:
cursor_widget = cursor_widget2
notebook_list.extend(notebook_list2)
@@ -515,9 +516,9 @@ class ParserForm(ParserInterface):
hpaned = gtk.HPaned()
container.wid_add(hpaned, colspan=int(attrs.get('colspan', 4)),
yexpand=True, yfill=True)
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
self.parse(model_name, node, fields, paned=hpaned,
tooltips=tooltips)
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
= self.parse(model_name, node, fields, paned=hpaned,
tooltips=tooltips)
if not cursor_widget:
cursor_widget = cursor_widget2
notebook_list.extend(notebook_list2)
@@ -531,9 +532,9 @@ class ParserForm(ParserInterface):
vpaned = gtk.VPaned()
container.wid_add(vpaned, colspan=int(attrs.get('colspan', 4)),
yexpand=True, yfill=True)
widget, widgets, buttons, spam, notebook_list, cursor_widget2 = \
self.parse(model_name, node, fields, paned=vpaned,
tooltips=tooltips)
widget, widgets, buttons, spam, notebook_list, cursor_widget2\
= self.parse(model_name, node, fields, paned=vpaned,
tooltips=tooltips)
if not cursor_widget:
cursor_widget = cursor_widget2
notebook_list.extend(notebook_list2)
@@ -544,9 +545,9 @@ class ParserForm(ParserInterface):
if 'position' in attrs:
vpaned.set_position(int(attrs['position']))
elif node.localName == 'child':
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
self.parse(model_name, node, fields, paned=paned,
tooltips=tooltips)
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
= self.parse(model_name, node, fields, paned=paned,
tooltips=tooltips)
if not cursor_widget:
cursor_widget = cursor_widget2
notebook_list.extend(notebook_list2)
@@ -568,7 +569,7 @@ class ParserForm(ParserInterface):
obj_id = self.screen.current_record.id
if obj_id < 0:
common.message(
_('You need to save the record before adding translations!'))
_('You need to save the record before adding translations!'))
return False
obj_id = self.screen.current_record.save(force_reload=False)
@@ -599,6 +600,7 @@ class ParserForm(ParserInterface):
widget_entry = widget_entry.get_children()[0]
if isinstance(widget_entry, gtk.ScrolledWindow):
widget_entry = widget_entry.get_child()
#widget accessor functions
def value_get(widget):
if isinstance(widget, gtk.Entry):
@@ -643,10 +645,9 @@ class ParserForm(ParserInterface):
else:
return None, False
parent = common.get_toplevel_window()
win = gtk.Dialog(_('Add Translation'),parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
win = gtk.Dialog(_('Add Translation'), parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
win.set_has_separator(True)
win.vbox.set_spacing(5)
win.set_property('default-width', 600)
@@ -657,7 +658,7 @@ class ParserForm(ParserInterface):
accel_group = gtk.AccelGroup()
win.add_accel_group(accel_group)
but_cancel = win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
but_ok = win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
but_ok.add_accelerator('clicked', accel_group, gtk.keysyms.Return,
@@ -689,17 +690,16 @@ class ParserForm(ParserInterface):
label.set_alignment(1.0, 0.5)
(entry, yoptions) = widget_duplicate(widget_entry)
hbox = gtk.HBox(homogeneous=False)
if code == lang['code']:
value_set(entry, value_get(widget_entry))
else:
value_set(entry, val[name])
entries_list.append((val['id'], lang['code'], entry))
table.attach(label, 0, 1, i, i+1, yoptions=False, xoptions=gtk.FILL,
ypadding=2, xpadding=3)
table.attach(entry, 1, 2, i, i+1, yoptions=yoptions,
ypadding=2, xpadding=3)
table.attach(label, 0, 1, i, i + 1, yoptions=False,
xoptions=gtk.FILL, ypadding=2, xpadding=3)
table.attach(entry, 1, 2, i, i + 1, yoptions=yoptions,
ypadding=2, xpadding=3)
i += 1
vbox.pack_start(table)
@@ -714,7 +714,6 @@ class ParserForm(ParserInterface):
win.vbox.add(scrolledwindow)
win.show_all()
data = []
response = win.run()
if response == gtk.RESPONSE_OK:
to_save = [(x[0], x[1], value_get(x[2])) for x in entries_list]
@@ -776,11 +775,11 @@ WIDGETS_TYPE = {
'one2many': (One2Many, 1, True, True),
'many2many': (Many2Many, 1, True, True),
'many2one': (Many2One, 1, False, False),
'email' : (Email, 1, False, False),
'url' : (URL, 1, False, False),
'callto' : (CallTo, 1, False, False),
'sip' : (SIP, 1, False, False),
'image' : (Image2, 1, False, False),
'email': (Email, 1, False, False),
'url': (URL, 1, False, False),
'callto': (CallTo, 1, False, False),
'sip': (SIP, 1, False, False),
'image': (Image2, 1, False, False),
'progressbar': (ProgressBar, 1, False, False),
'one2one': (One2One, 1, False, False),
}
@@ -88,7 +88,7 @@ class Reference(WidgetInterface):
if selection:
pop = sorted((len(x) for x in selection), reverse=True)
average = sum(pop) / len(pop)
deviation = int(math.sqrt(sum((x - average)**2 for x in pop) /
deviation = int(math.sqrt(sum((x - average) ** 2 for x in pop) /
len(pop)))
width = max(next((x for x in pop if x < (deviation * 4)), 10), 10)
else:
@@ -149,7 +149,6 @@ class Reference(WidgetInterface):
def sig_focus_out(self, widget, event, leave=False):
if not self.focus_out:
return
child = self.widget_combo.get_child()
self.changed = False
value = self.field.get_client(self.record)
@@ -166,6 +165,7 @@ class Reference(WidgetInterface):
if not leave:
screen = Screen(model, mode=['form'])
screen.load([obj_id])
def callback(result):
if result and screen.save_current():
value = (screen.current_record.id,
@@ -178,7 +178,7 @@ class Reference(WidgetInterface):
WinForm(screen, callback)
return
elif model:
if not self._readonly and ( self.wid_text.get_text() or not leave):
if not self._readonly and (self.wid_text.get_text() or not leave):
domain = self.field.domain_get(self.record)
context = self.field.context_get(self.record)
@@ -205,7 +205,8 @@ class Reference(WidgetInterface):
def callback(ids):
if ids:
self.field.set_client(self.record, (model, (ids[0], '')))
self.field.set_client(self.record,
(model, (ids[0], '')))
self.focus_out = True
self.changed = True
self.display(self.record, self.field)
@@ -223,6 +224,7 @@ class Reference(WidgetInterface):
if not model:
return
screen = Screen(model, mode=['form'])
def callback(result):
if result and screen.save_current():
value = (screen.current_record.id,
@@ -238,7 +240,8 @@ class Reference(WidgetInterface):
elif event.keyval == gtk.keysyms.F2:
self.sig_focus_out(widget, event)
return True
elif event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return) and editable:
elif (event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return)
and editable):
if self.field.get(self.record) or \
not self.wid_text.get_text():
return False
@@ -272,7 +275,7 @@ class Reference(WidgetInterface):
if not field:
child.set_text('')
child.set_position(0)
self.changed =True
self.changed = True
return False
super(Reference, self).display(record, field)
value = field.get_client(record)
@@ -306,8 +309,8 @@ class Reference(WidgetInterface):
if self.last_key[0] == key:
self.last_key[1] += 1
else:
self.last_key = [ key, 1 ]
if not self.key_catalog.has_key(key):
self.last_key = [key, 1]
if key not in self.key_catalog:
return
self.widget_combo.set_active_iter(
self.key_catalog[key][self.last_key[1] \
@@ -7,7 +7,6 @@ import math
from interface import WidgetInterface
import tryton.rpc as rpc
import tryton.common as common
from tryton.pyson import PYSONDecoder
from tryton.exceptions import TrytonServerError
@@ -61,8 +60,8 @@ class Selection(WidgetInterface):
if domain == self._last_domain:
return
args = ('model', self.attrs['relation'], 'search_read', domain, 0, None,
None, ['rec_name'], rpc.CONTEXT)
args = ('model', self.attrs['relation'], 'search_read', domain, 0,
None, None, ['rec_name'], rpc.CONTEXT)
try:
result = rpc.execute(*args)
except TrytonServerError, exception:
@@ -101,14 +100,15 @@ class Selection(WidgetInterface):
if self._selection:
pop = sorted((len(x) for x in self._selection), reverse=True)
average = sum(pop) / len(pop)
deviation = int(math.sqrt(sum((x - average)**2 for x in pop) /
deviation = int(math.sqrt(sum((x - average) ** 2 for x in pop) /
len(pop)))
width = max(next((x for x in pop if x < (deviation * 4)), 10), 10)
else:
width = 10
self.entry.child.set_width_chars(width)
if self._selection:
self.entry.child.set_max_length(max(len(x) for x in self._selection))
self.entry.child.set_max_length(
max(len(x) for x in self._selection))
completion.set_text_column(0)
return lst
@@ -2,7 +2,6 @@
#this repository contains the full copyright notices and license terms.
import gtk
from interface import WidgetInterface
import tryton.rpc as rpc
from tryton.config import CONFIG
try:
@@ -28,7 +27,8 @@ class TextBox(WidgetInterface):
#TODO better tab solution
self.textview.set_accepts_tab(False)
self.textview.connect('focus-in-event', lambda x, y: self._focus_in())
self.textview.connect('focus-out-event', lambda x, y: self._focus_out())
self.textview.connect('focus-out-event',
lambda x, y: self._focus_out())
self.scrolledwindow.add(self.textview)
self.scrolledwindow.show_all()
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gettext
import gtk
from char import Char
import webbrowser
@@ -104,4 +103,3 @@ class SIP(URL):
else:
self.tooltips.set_tip(self.button, '')
self.tooltips.disable()
@@ -1,8 +1,9 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
#This code is inspired by the pycha project (http://www.lorenzogil.com/projects/pycha/)
#This code is inspired by the pycha project
#(http://www.lorenzogil.com/projects/pycha/)
from graph import Graph
from tryton.common import hex2rgb, lighten, float_time_to_text
from tryton.common import float_time_to_text
import locale
import math
import cairo
@@ -21,8 +22,8 @@ class Bar(Graph):
w = self.area.w * bar.w
h = self.area.h * bar.h
if w < 1 or h <1:
return # don't draw too small
if w < 1 or h < 1:
return # don't draw too small
cr.set_source_rgba(0, 0, 0, 0.15)
rectangle = self._getShadowRectangle(x, y, w, h)
@@ -52,7 +53,8 @@ class Bar(Graph):
def sourceRectangle(self, x, y, w, h, r, g, b):
linear = cairo.LinearGradient((x + w) / 2, y, (x + w) / 2, y + h)
linear.add_color_stop_rgb(0, 3.5 * r / 5.0, 3.5 * g / 5.0, 3.5 * b / 5.0)
linear.add_color_stop_rgb(0, 3.5 * r / 5.0, 3.5 * g / 5.0,
3.5 * b / 5.0)
linear.add_color_stop_rgb(1, r, g, b)
return linear
@@ -71,8 +73,9 @@ class Bar(Graph):
highlight = False
draw_bars = []
yfields_float_time = dict([(x.get('key', x['name']), x.get('float_time'))
for x in self.yfields if x.get('widget')])
yfields_float_time = dict(
(x.get('key', x['name']), x.get('float_time'))
for x in self.yfields if x.get('widget'))
for bar in self.bars:
if intersect(bar, event):
if not bar.highlight:
@@ -179,7 +182,7 @@ class VerticalBar(Bar):
return ylabels
def _getShadowRectangle(self, x, y, w, h):
return (x-2, y-2, w+4, h+2)
return (x - 2, y - 2, w + 4, h + 2)
class HorizontalBar(Bar):
@@ -236,7 +239,7 @@ class HorizontalBar(Bar):
return [(x[0], x[1]) for x in ylabels]
def _getShadowRectangle(self, x, y, w, h):
return (x, y-2, w+2, h+4)
return (x, y - 2, w + 2, h + 4)
def _getLegendPosition(self, width, height):
return self.area.x + self.area.w * 0.95 - width, \
@@ -1,20 +1,19 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
#This code is inspired by the pycha project (http://www.lorenzogil.com/projects/pycha/)
#This code is inspired by the pycha project
#(http://www.lorenzogil.com/projects/pycha/)
import gtk
from functools import reduce
from tryton.common import hex2rgb, generateColorscheme, DT_FORMAT, \
DHM_FORMAT, COLOR_SCHEMES, datetime_strftime
COLOR_SCHEMES, datetime_strftime
from tryton.pyson import PYSONDecoder
import locale
import math
import datetime
import time
from dateutil.relativedelta import relativedelta
import tryton.rpc as rpc
import cairo
from tryton.action import Action
from tryton.translate import date_format
from tryton.gui.window import Window
@@ -65,10 +64,11 @@ class Popup(object):
def enter(self, widget, event):
self.win.hide()
class Graph(gtk.DrawingArea):
'Graph'
__gsignals__ = { "expose-event": "override" }
__gsignals__ = {"expose-event": "override"}
def __init__(self, xfield, yfields, attrs, model):
super(Graph, self).__init__()
@@ -167,13 +167,12 @@ class Graph(gtk.DrawingArea):
def drawGraph(self, cr, width, height):
pass
def YLabels(self):
ylabels = []
if self.yrange == 0.0:
base = 1
else:
base = 10**int(math.log(self.yrange, 10))
base = 10 ** int(math.log(self.yrange, 10))
for i in xrange(int(self.yrange / base) + 1):
val = int(self.minyval / base) * base + i * base
h = (val - self.minyval) * self.yscale
@@ -356,7 +355,6 @@ class Graph(gtk.DrawingArea):
self.ids[x].append(model.id)
self.datas.setdefault(x, {})
for yfield in self.yfields:
name = yfield['name']
key = yfield.get('key', yfield['name'])
self.datas[x].setdefault(key, 0.0)
if yfield.get('domain'):
@@ -411,7 +409,8 @@ class Graph(gtk.DrawingArea):
yLabelWidth = 0
width = width - self.leftPadding - yLabelWidth - self.rightPadding
height = height - self.topPadding - self.bottomPadding - xLabelHeight
self.area = Area(self.leftPadding + yLabelWidth, self.topPadding, width, height)
self.area = Area(self.leftPadding + yLabelWidth, self.topPadding,
width, height)
def updateXY(self):
self.maxxval = len(self.datas)
@@ -458,6 +457,7 @@ class Graph(gtk.DrawingArea):
def _getDatasKeys(self):
return [x.get('key', x['name']) for x in self.yfields]
class Area(object):
def __init__(self, x, y, w, h):
@@ -1,6 +1,7 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
#This code is inspired by the pycha project (http://www.lorenzogil.com/projects/pycha/)
#This code is inspired by the pycha project
#(http://www.lorenzogil.com/projects/pycha/)
from graph import Graph
from tryton.common import hex2rgb, float_time_to_text, safe_eval
import locale
@@ -33,8 +34,8 @@ class Line(Graph):
if self.xrange == 0:
x = 1.0
if not bool(safe_eval(yfield2attrs[yfield].get('empty', '1'))) \
and yval == 0:
if (not bool(safe_eval(yfield2attrs[yfield].get('empty', '1')))
and yval == 0):
continue
point = Point(x, y, xval, yval, xfield, yfield)
@@ -140,8 +141,9 @@ class Line(Graph):
highlight = False
draw_points = []
yfields_float_time = dict([(x.get('key', x['name']), x.get('float_time'))
for x in self.yfields if x.get('widget')])
yfields_float_time = dict(
(x.get('key', x['name']), x.get('float_time'))
for x in self.yfields if x.get('widget'))
for point in self.points:
if point == nearest[0] and nearest[1] < dia / 100:
if not point.highlight:
@@ -3,12 +3,12 @@
from tryton.gui.window.view_form.view.interface import ParserInterface
import tryton.common as common
import gtk
from graph import Graph
from bar import VerticalBar, HorizontalBar
from line import Line
from pie import Pie
from tryton.config import TRYTON_ICON, CONFIG
import sys, os
import sys
import os
import gettext
_ = gettext.gettext
@@ -20,12 +20,12 @@ GRAPH_TYPE = {
'pie': Pie,
}
def save(widget, graph):
parent = common.get_toplevel_window()
dia = gtk.Dialog(_('Save As'), parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
dia.set_icon(TRYTON_ICON)
dia.set_has_separator(True)
dia.set_default_response(gtk.RESPONSE_OK)
@@ -98,6 +98,7 @@ def save(widget, graph):
dia.destroy()
return
def button_press(widget, event, graph):
if event.button == 3:
menu = gtk.Menu()
@@ -132,7 +133,8 @@ class ParserGraph(ParserInterface):
continue
xfield = common.node_attributes(child)
if not xfield.get('string'):
xfield['string'] = fields[xfield['name']].attrs['string']
xfield['string'] = fields[xfield['name']
].attrs['string']
break
elif node.localName == 'y':
for child in node.childNodes:
@@ -140,10 +142,12 @@ class ParserGraph(ParserInterface):
continue
yattrs = common.node_attributes(child)
if not yattrs.get('string') and yattrs['name'] != '#':
yattrs['string'] = fields[yattrs['name']].attrs['string']
yattrs['string'] = fields[yattrs['name']
].attrs['string']
yfields.append(yattrs)
widget = GRAPH_TYPE[attrs.get('type', 'vbar')](xfield, yfields, attrs, model)
widget = GRAPH_TYPE[attrs.get('type', 'vbar')
](xfield, yfields, attrs, model)
event = gtk.EventBox()
event.add(widget)
event.connect('button-press-event', button_press, widget)
@@ -1,6 +1,7 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
#This code is inspired by the pycha project (http://www.lorenzogil.com/projects/pycha/)
#This code is inspired by the pycha project
#(http://www.lorenzogil.com/projects/pycha/)
from graph import Graph, Area
import math
import cairo
@@ -97,7 +98,8 @@ class Pie(Graph):
cr.set_source_rgba(*color)
slice.draw(cr, self.centerx, self.centery, self.radius)
cr.fill()
cr.set_source_rgb(*hex2rgb(self.attrs.get('background', '#f5f5f5')))
cr.set_source_rgb(*hex2rgb(
self.attrs.get('background', '#f5f5f5')))
slice.draw(cr, self.centerx, self.centery, self.radius)
cr.set_line_width(2)
cr.stroke()
@@ -120,7 +122,8 @@ class Pie(Graph):
if event.y == self.centery:
angle = math.pi / 2
else:
angle = math.atan((event.x - self.centerx)/(self.centery - event.y))
angle = math.atan((event.x - self.centerx)
/ (self.centery - event.y))
if event.x >= self.centerx:
if event.y <= self.centery:
pass
@@ -139,12 +142,14 @@ class Pie(Graph):
if self.yfields[0].get('widget') == 'float_time':
conv = None
if self.yfields[0].get('float_time'):
conv = rpc.CONTEXT.get(self.yfields[0]['float_time'])
conv = rpc.CONTEXT.get(
self.yfields[0]['float_time'])
value = float_time_to_text(slice.fraction * self.sum,
conv)
sum = float_time_to_text(self.sum, conv)
else:
value = locale.format('%.2f', slice.fraction * self.sum)
value = locale.format('%.2f',
slice.fraction * self.sum)
sum = locale.format('%.2f', self.sum)
label = '%s (%s%%)\n%s/%s' % (self.labels[slice.xname],
locale.format('%.2f', slice.fraction * 100),
@@ -181,8 +186,8 @@ class Slice(object):
cr.new_path()
cr.move_to(centerx, centery)
cr.arc(centerx, centery, radius,
self.startAngle - (math.pi/2),
self.endAngle - (math.pi/2))
self.startAngle - (math.pi / 2),
self.endAngle - (math.pi / 2))
cr.line_to(centerx, centery)
cr.close_path()
+55 -43
View File
@@ -21,7 +21,6 @@ from tryton.common.cellrendererbinary import CellRendererBinary
from tryton.pyson import PYSONEncoder
from tryton.gui.window import Window
from tryton.exceptions import TrytonServerError
import os
_ = gettext.gettext
@@ -34,7 +33,8 @@ def path_convert_id2pos(model, id_path):
current_id = id_path.pop(0)
try:
record = group.get(current_id)
group = record.children_group(model.children_field, check_load=True)
group = record.children_group(model.children_field,
check_load=True)
except (KeyError, AttributeError):
return None
return model.on_get_path(record)
@@ -49,7 +49,7 @@ class AdaptModelGroup(gtk.GenericTreeModel):
self.sort_asc = True
self.set_property('leak_references', False)
self.children_field = children_field
self.__removed = None # XXX dirty hack to allow update of has_child
self.__removed = None # XXX dirty hack to allow update of has_child
def added(self, group, record):
if (group is self.group
@@ -80,7 +80,7 @@ class AdaptModelGroup(gtk.GenericTreeModel):
len(record.children_group(self.children_field)) <= 1):
path = self.on_get_path(record.parent)
iter_ = self.get_iter(path)
self.__removed = record # XXX check for thread
self.__removed = record # XXX check for thread
self.row_has_child_toggled(path, iter_)
self.__removed = None
@@ -313,30 +313,34 @@ class ViewList(ParserView):
if screen.readonly:
dnd = False
if dnd:
self.widget_tree.drag_source_set(gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON3_MASK,
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),],
gtk.gdk.ACTION_MOVE)
self.widget_tree.drag_source_set(
gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON3_MASK,
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0)],
gtk.gdk.ACTION_MOVE)
self.widget_tree.drag_dest_set(gtk.DEST_DEFAULT_ALL,
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),],
gtk.gdk.ACTION_MOVE)
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0)],
gtk.gdk.ACTION_MOVE)
self.widget_tree.connect('drag-begin', self.drag_begin)
self.widget_tree.connect('drag-motion', self.drag_motion)
self.widget_tree.connect('drag-drop', self.drag_drop)
self.widget_tree.connect("drag-data-get", self.drag_data_get)
self.widget_tree.connect('drag-data-received', self.drag_data_received)
self.widget_tree.connect('drag-data-received',
self.drag_data_received)
self.widget_tree.connect('drag-data-delete', self.drag_data_delete)
self.widget_tree.connect('key_press_event', self.on_keypress)
if self.children_field:
self.widget_tree.connect('test-expand-row', self.test_expand_row)
self.widget_tree.set_expander_column(self.widget_tree.get_column(0))
self.widget_tree.set_expander_column(
self.widget_tree.get_column(0))
def get_fields(self):
return [col.name for col in self.widget_tree.get_columns() if col.name]
def on_keypress(self, widget, event):
if event.keyval == gtk.keysyms.c and event.state & gtk.gdk.CONTROL_MASK:
if (event.keyval == gtk.keysyms.c
and event.state & gtk.gdk.CONTROL_MASK):
self.on_copy()
return False
if event.keyval in (gtk.keysyms.Down, gtk.keysyms.Up):
@@ -347,7 +351,7 @@ class ViewList(ParserView):
if event.keyval == gtk.keysyms.Down:
test = True
for i in xrange(len(path)):
iter_ = model.get_iter(path[0:i+1])
iter_ = model.get_iter(path[0:i + 1])
if model.iter_next(iter_):
test = False
if test:
@@ -390,7 +394,7 @@ class ViewList(ParserView):
try:
for field in fields:
record.__getitem__(field, True)
except TrytonServerError, exception:
except TrytonServerError:
return True
iter_ = model.iter_next(iter_)
return False
@@ -419,7 +423,7 @@ class ViewList(ParserView):
clipboard.set_with_data(targets, self.copy_get_func,
self.copy_clear_func, selection)
def copy_foreach(self, treemodel, path,iter, data):
def copy_foreach(self, treemodel, path, iter, data):
record = treemodel.get_value(iter, 0)
values = []
for col in self.widget_tree.get_columns():
@@ -465,6 +469,7 @@ class ViewList(ParserView):
def drag_data_get(self, treeview, context, selection, target_id,
etime):
treeview.emit_stop_by_name('drag-data-get')
def _func_sel_get(store, path, iter_, data):
value = store.get_value(iter_, 0)
data.append(json.dumps(value.get_path(store.group)))
@@ -496,6 +501,7 @@ class ViewList(ParserView):
record = store.group.get_by_path(data)
record_path = store.on_get_path(record)
drop_info = treeview.get_dest_row_at_pos(x, y)
def check_recursion(from_, to):
if not from_ or not to:
return True
@@ -508,7 +514,8 @@ class ViewList(ParserView):
if drop_info:
path, position = drop_info
check_path = path
if position in (gtk.TREE_VIEW_DROP_BEFORE, gtk.TREE_VIEW_DROP_AFTER):
if position in (gtk.TREE_VIEW_DROP_BEFORE,
gtk.TREE_VIEW_DROP_AFTER):
check_path = path[:-1]
if not check_recursion(record_path, check_path):
return
@@ -634,28 +641,32 @@ class ViewList(ParserView):
def __getitem__(self, name):
return None
def destroy(self):
if CONFIG['client.save_width_height']:
fields = {}
last_col = None
for col in self.widget_tree.get_columns():
if col.get_visible():
last_col = col
if not hasattr(col, 'name') or not hasattr(col, 'width'):
continue
if col.get_width() != col.width and col.get_visible():
fields[col.name] = col.get_width()
#Don't set width for last visible columns
#as it depends of the screen size
if last_col and last_col.name in fields:
del fields[last_col.name]
def save_width_height(self):
if not CONFIG['client.save_width_height']:
return
fields = {}
last_col = None
for col in self.widget_tree.get_columns():
if col.get_visible():
last_col = col
if not hasattr(col, 'name') or not hasattr(col, 'width'):
continue
if col.get_width() != col.width and col.get_visible():
fields[col.name] = col.get_width()
#Don't set width for last visible columns
#as it depends of the screen size
if last_col and last_col.name in fields:
del fields[last_col.name]
if fields and any(fields.itervalues()):
try:
rpc.execute('model', 'ir.ui.view_tree_width', 'set_width',
self.screen.model_name, fields, rpc.CONTEXT)
except TrytonServerError:
pass
if fields and any(fields.itervalues()):
try:
rpc.execute('model', 'ir.ui.view_tree_width', 'set_width',
self.screen.model_name, fields, rpc.CONTEXT)
except TrytonServerError:
pass
def destroy(self):
self.save_width_height()
self.widget_tree.destroy()
self.screen = None
self.widget_tree = None
@@ -710,7 +721,6 @@ class ViewList(ParserView):
return True
self.update_children()
def set_value(self):
if hasattr(self.widget_tree, 'editable') \
and self.widget_tree.editable:
@@ -773,11 +783,12 @@ class ViewList(ParserView):
value += field_value
if loaded:
label_str = locale.format('%.' + str(self.children[child][3]) + 'f',
value_selected, True)
label_str = locale.format(
'%.' + str(self.children[child][3]) + 'f', value_selected,
True)
label_str += ' / '
label_str += locale.format('%.' + str(self.children[child][3]) + 'f',
value, True)
label_str += locale.format(
'%.' + str(self.children[child][3]) + 'f', value, True)
else:
label_str = '-'
self.children[child][2].set_text(label_str)
@@ -807,7 +818,8 @@ class ViewList(ParserView):
break
if path[:-1]:
self.widget_tree.expand_to_path(path[:-1])
self.widget_tree.scroll_to_cell(path, focus_column, use_align=False)
self.widget_tree.scroll_to_cell(path, focus_column,
use_align=False)
self.widget_tree.set_cursor(path, focus_column, new)
def sel_ids_get(self):
@@ -38,7 +38,6 @@ class EditableTreeView(gtk.TreeView):
def on_open_remote(self, current_record, fieldname, create, value,
entry=None, callback=None):
field = current_record[fieldname]
cell = self.cells[fieldname]
if value != cell.get_textual_value(current_record) or not value:
changed = True
@@ -151,6 +150,7 @@ class EditableTreeView(gtk.TreeView):
else:
txt = entry.get_active_text()
entry.handler_block(entry.editing_done_id)
def callback():
entry.handler_unblock(entry.editing_done_id)
self.set_cursor(path, column, True)
@@ -201,9 +201,9 @@ class EditableTreeView(gtk.TreeView):
break
entry.handler_block(entry.editing_done_id)
if self.editable == 'top':
new_path = self._key_up(path, model, col)
self._key_up(path, model, col)
else:
new_path = self._key_down(path, model, column)
self._key_down(path, model, column)
entry.handler_unblock(entry.editing_done_id)
elif event.keyval in (gtk.keysyms.F3, gtk.keysyms.F2):
if isinstance(entry, gtk.Entry):
@@ -211,6 +211,7 @@ class EditableTreeView(gtk.TreeView):
else:
value = entry.get_active_text()
entry.handler_block(entry.editing_done_id)
def callback():
cell = self.cells[column.name]
value = cell.get_textual_value(record)
@@ -221,13 +222,14 @@ class EditableTreeView(gtk.TreeView):
entry.handler_unblock(entry.editing_done_id)
self.set_cursor(path, column, True)
self.on_open_remote(record, column.name,
create=(event.keyval==gtk.keysyms.F3), value=value,
create=(event.keyval == gtk.keysyms.F3), value=value,
callback=callback)
else:
field = record[column.name]
if isinstance(entry, gtk.Entry):
entry.set_max_length(int(field.attrs.get('size', 0)))
# store in the record the entry widget to get the value in set_value
# store in the record the entry widget to get the value in
# set_value
field.editabletree_entry = entry
record.modified_fields.setdefault(column.name)
return False
@@ -12,7 +12,7 @@ from tryton.gui.window.view_form.screen import Screen
from tryton.config import CONFIG
import tryton.rpc as rpc
from tryton.common import DT_FORMAT, DHM_FORMAT, COLORS, node_attributes, \
TRYTON_ICON, HM_FORMAT, file_selection, file_open
HM_FORMAT, file_selection, file_open
import tryton.common as common
from tryton.exceptions import TrytonError, TrytonServerError
from tryton.common.cellrendererbutton import CellRendererButton
@@ -25,7 +25,6 @@ from tryton.common.cellrendererfloat import CellRendererFloat
from tryton.common.cellrendererbinary import CellRendererBinary
from tryton.action import Action
from tryton.translate import date_format
from tryton.pyson import PYSONDecoder
import gtk
import locale
import datetime
@@ -35,6 +34,7 @@ import operator
_ = gettext.gettext
def send_keys(renderer, editable, position, treeview):
editable.connect('key_press_event', treeview.on_keypressed)
editable.editing_done_id = editable.connect('editing_done',
@@ -42,6 +42,7 @@ def send_keys(renderer, editable, position, treeview):
if isinstance(editable, (gtk.ComboBoxEntry, gtk.ComboBox)):
editable.connect('changed', treeview.on_editing_done)
def sort_model(column, treeview, screen):
for col in treeview.get_columns():
if col != column:
@@ -71,6 +72,7 @@ def sort_model(column, treeview, screen):
for record in unsaved_records:
store.group.append(record)
class ParserTree(ParserInterface):
def __init__(self, parent=None, attrs=None, screen=None,
@@ -142,6 +144,7 @@ class ParserTree(ParserInterface):
render_pixbuf = gtk.CellRendererPixbuf()
col.pack_start(render_pixbuf, expand=False)
icon = node_attrs['icon']
def setter(column, cell, store, iter):
record = store.get_value(iter, 0)
value = record[icon].get_client(record) or ''
@@ -242,6 +245,7 @@ class ParserTree(ParserInterface):
col.set_widget(label)
col._type = 'button'
col.set_cell_data_func(renderer, cell.setter)
if 'width' in node_attrs:
width = int(node_attrs['width'])
else:
@@ -328,7 +332,8 @@ class Char(object):
bg_color = 'white'
if not field.get_state_attrs(record).get('valid', True):
bg_color = COLORS.get('invalid', 'white')
elif bool(int(field.get_state_attrs(record).get('required', 0))):
elif bool(int(
field.get_state_attrs(record).get('required', 0))):
bg_color = COLORS.get('required', 'white')
cell.set_property('background', bg_color)
if bg_color == 'white':
@@ -366,6 +371,7 @@ class Char(object):
if callback:
callback()
class Int(Char):
def __init__(self, field_name, model_name, treeview, attrs=None):
@@ -383,6 +389,7 @@ class Int(Char):
return locale.format('%d',
record[self.field_name].get_client(record) or 0, True)
class Boolean(Int):
def __init__(self, field_name, model_name, treeview, attrs=None):
@@ -484,7 +491,7 @@ class Float(Char):
def get_textual_value(self, record):
field = record[self.field_name]
digit = record.expr_eval(field.attrs.get('digits', (16, 2)))[1]
return locale.format('%.'+str(digit)+'f',
return locale.format('%.' + str(digit) + 'f',
record[self.field_name].get_client(record) or 0.0, True)
def value_from_text(self, record, text, callback=None):
@@ -513,7 +520,9 @@ class FloatTime(Char):
def value_from_text(self, record, text, callback=None):
field = record[self.field_name]
field.set_client(record, common.text_to_float_time(text, self.conv))
digits = record.expr_eval(field.attrs.get('digits', (16, 2)))
field.set_client(record,
round(common.text_to_float_time(text, self.conv), digits[1]))
if callback:
callback()
@@ -582,7 +591,7 @@ class Binary(Char):
filename = file_selection(_('Save As...'), filename=filename,
action=gtk.FILE_CHOOSER_ACTION_SAVE)
if filename:
with open(filename,'wb') as fp:
with open(filename, 'wb') as fp:
fp.write(field.get_data(record))
def clear_binary(self, renderer, path):
@@ -681,6 +690,7 @@ class M2O(Char):
def search_remote(self, record, relation, ids=None, domain=None,
context=None, callback=None):
field = record.group.fields[self.field_name]
def search_callback(found):
value = None
if found:
@@ -733,6 +743,7 @@ class O2M(Char):
screen = Screen(relation, mode=['tree', 'form'],
exclude_field=field.attrs.get('relation_field'))
screen.group = group
def open_callback(result):
if callback:
callback()
@@ -781,6 +792,7 @@ class M2M(Char):
return
if not callback:
return
def winsearch_callback(result):
field.set_client(record, result or [])
if callback:
@@ -807,20 +819,21 @@ class M2M(Char):
field.set_client(record, False)
if callback:
callback()
if ids and len(ids)==1:
if ids and len(ids) == 1:
field.set_client(record, ids)
if callback:
callback()
return
else:
ids = [x.id for x in field.get_client(record)]
def open_callback(result):
if result:
field.set_client(record, result)
if callback:
callback()
WinSearch(relation, open_callback, sel_multi=True, ids=ids, context=context,
domain=domain)
WinSearch(relation, open_callback, sel_multi=True, ids=ids,
context=context, domain=domain)
class Selection(Char):
@@ -1002,6 +1015,15 @@ class Button(object):
self.renderer.connect('clicked', self.button_clicked)
def setter(self, column, cell, store, iter):
record = store.get_value(iter, 0)
states = record.expr_eval(self.attrs.get('states', {}),
check_load=False)
invisible = states.get('invisible', False)
cell.set_property('visible', not invisible)
# TODO readonly
# TODO icon
def button_clicked(self, widget, path):
if not path:
return True
@@ -6,6 +6,7 @@ import tryton.common as common
_ = gettext.gettext
class ScreenContainer(object):
def __init__(self):
@@ -139,7 +140,7 @@ class ScreenContainer(object):
model.append([r.strip()])
def clear(self, widget=None):
self.search_entry.set_text('');
self.search_entry.set_text('')
def get_text(self):
return self.search_entry.get_text().strip().decode('utf-8')
+5 -7
View File
@@ -20,11 +20,9 @@ class WinExport(object):
def __init__(self, model, ids, context=None):
self.parent = common.get_toplevel_window()
self.dialog = gtk.Dialog(
title= _("Export to CSV"),
parent=self.parent,
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
| gtk.WIN_POS_CENTER_ON_PARENT)
self.dialog = gtk.Dialog(title=_("Export to CSV"), parent=self.parent,
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |
gtk.WIN_POS_CENTER_ON_PARENT)
self.dialog.set_icon(TRYTON_ICON)
vbox = gtk.VBox()
@@ -327,7 +325,7 @@ class WinExport(object):
try:
new_id = rpc.execute(*args)
except TrytonServerError, exception:
new_ids = common.process_exception(exception, self.dialog, *args)
new_id = common.process_exception(exception, self.dialog, *args)
if not new_id:
return
self.predef_model.append((
@@ -419,7 +417,7 @@ class WinExport(object):
row = []
for val in data:
if type(val) == types.StringType:
row.append(val.replace('\n',' ').replace('\t',' '))
row.append(val.replace('\n', ' ').replace('\t', ' '))
else:
row.append(val)
writer.writerow(row)
+15 -10
View File
@@ -1,6 +1,6 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from tryton.common import TRYTON_ICON
from tryton.common import TRYTON_ICON, COLOR_SCHEMES
import tryton.common as common
from tryton.config import CONFIG
import gtk
@@ -8,6 +8,7 @@ import pango
import gettext
from tryton.exceptions import TrytonServerError
from tryton.gui.window.nomodal import NoModal
import tryton.rpc as rpc
_ = gettext.gettext
@@ -56,7 +57,7 @@ class WinForm(NoModal):
if new and many:
self.but_ok.add_accelerator('clicked',
self.accel_group, gtk.keysyms.Return,
gtk.gdk.CONTROL_MASK|gtk.gdk.SHIFT_MASK,
gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK,
gtk.ACCEL_VISIBLE)
self.but_new = self.win.add_button(gtk.STOCK_NEW,
@@ -130,7 +131,8 @@ class WinForm(NoModal):
tooltips.set_tip(self.but_del, _('Delete selected record'))
self.but_del.connect('clicked', self._sig_remove)
img_del = gtk.Image()
img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_del.set_from_stock('tryton-delete',
gtk.ICON_SIZE_SMALL_TOOLBAR)
img_del.set_alignment(0.5, 0.5)
self.but_del.add(img_del)
self.but_del.set_relief(gtk.RELIEF_NONE)
@@ -142,7 +144,8 @@ class WinForm(NoModal):
tooltips.set_tip(but_pre, _('Previous'))
but_pre.connect('clicked', self._sig_previous)
img_pre = gtk.Image()
img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_pre.set_from_stock('tryton-go-previous',
gtk.ICON_SIZE_SMALL_TOOLBAR)
img_pre.set_alignment(0.5, 0.5)
but_pre.add(img_pre)
but_pre.set_relief(gtk.RELIEF_NONE)
@@ -155,7 +158,8 @@ class WinForm(NoModal):
tooltips.set_tip(but_next, _('Next'))
but_next.connect('clicked', self._sig_next)
img_next = gtk.Image()
img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_next.set_from_stock('tryton-go-next',
gtk.ICON_SIZE_SMALL_TOOLBAR)
img_next.set_alignment(0.5, 0.5)
but_next.add(img_next)
but_next.set_relief(gtk.RELIEF_NONE)
@@ -167,7 +171,8 @@ class WinForm(NoModal):
tooltips.set_tip(but_switch, _('Switch'))
but_switch.connect('clicked', self.switch_view)
img_switch = gtk.Image()
img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_switch.set_from_stock('tryton-fullscreen',
gtk.ICON_SIZE_SMALL_TOOLBAR)
img_switch.set_alignment(0.5, 0.5)
but_switch.add(img_switch)
but_switch.set_relief(gtk.RELIEF_NONE)
@@ -251,6 +256,7 @@ class WinForm(NoModal):
from tryton.gui.window.win_search import WinSearch
domain = []
context = rpc.CONTEXT.copy()
model_name = self.screen.model_name
try:
if self.wid_text.get_text():
@@ -258,7 +264,7 @@ class WinForm(NoModal):
'%' + self.wid_text.get_text() + '%'), domain]
else:
dom = domain
ids = rpc.execute('model', self.attrs['relation'], 'search', dom,
ids = rpc.execute('model', model_name, 'search', dom,
0, CONFIG['client.limit'], None, context)
except TrytonServerError, exception:
common.process_exception(exception)
@@ -274,9 +280,8 @@ class WinForm(NoModal):
self.wid_text.set_text('')
if len(ids) != 1:
WinSearch(self.attrs['relation'], callback, sel_multi=True,
ids=ids, context=context, domain=domain,
views_preload=self.attrs.get('views', {}))
WinSearch(model_name, callback, sel_multi=True,
ids=ids, context=context, domain=domain)
else:
callback(ids)
+10 -11
View File
@@ -45,7 +45,7 @@ class WinImport(object):
hbox_mapping.pack_start(vbox_buttons, False, True, 0)
button_add = gtk.Button(_("_Add"), stock=None, use_underline=True)
button_add.set_alignment(0.0,0.0)
button_add.set_alignment(0.0, 0.0)
img_button = gtk.Image()
img_button.set_from_stock('tryton-list-add', gtk.ICON_SIZE_BUTTON)
button_add.set_image(img_button)
@@ -54,7 +54,7 @@ class WinImport(object):
button_remove = gtk.Button(_("_Remove"), stock=None,
use_underline=True)
button_remove.set_alignment(0.0,0.0)
button_remove.set_alignment(0.0, 0.0)
img_button = gtk.Image()
img_button.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_BUTTON)
button_remove.set_image(img_button)
@@ -63,7 +63,7 @@ class WinImport(object):
button_remove_all = gtk.Button(_("Clear"), stock=None,
use_underline=True)
button_remove_all.set_alignment(0.0,0.0)
button_remove_all.set_alignment(0.0, 0.0)
img_button = gtk.Image()
img_button.set_from_stock('tryton-clear', gtk.ICON_SIZE_BUTTON)
button_remove_all.set_image(img_button)
@@ -75,7 +75,7 @@ class WinImport(object):
button_autodetect = gtk.Button(_("Auto-Detect"), stock=None,
use_underline=True)
button_autodetect.set_alignment(0.0,0.0)
button_autodetect.set_alignment(0.0, 0.0)
img_button = gtk.Image()
img_button.set_from_stock('tryton-find', gtk.ICON_SIZE_BUTTON)
button_autodetect.set_image(img_button)
@@ -132,15 +132,15 @@ class WinImport(object):
table.attach(self.import_csv_sep, 1, 2, 0, 1)
label_import_csv_del = gtk.Label(_("Text Delimiter:"))
label_import_csv_del.set_alignment( 1, 0.5)
label_import_csv_del.set_alignment(1, 0.5)
table.attach(label_import_csv_del, 2, 3, 0, 1)
self.import_csv_del = gtk.Entry()
self.import_csv_del.set_text( "\"")
self.import_csv_del.set_width_chars( 1)
self.import_csv_del.set_text("\"")
self.import_csv_del.set_width_chars(1)
table.attach(self.import_csv_del, 3, 4, 0, 1)
label_import_csv_enc = gtk.Label(_("Encoding:"))
label_import_csv_enc.set_alignment( 1, 0.5)
label_import_csv_enc.set_alignment(1, 0.5)
table.attach(label_import_csv_enc, 0, 1, 1, 2)
self.import_csv_enc = gtk.combo_box_new_text()
self.import_csv_enc.append_text("UTF-8")
@@ -148,7 +148,7 @@ class WinImport(object):
table.attach(self.import_csv_enc, 1, 2, 1, 2)
label_import_csv_skip = gtk.Label(_("Lines to Skip:"))
label_import_csv_skip.set_alignment( 1, 0.5)
label_import_csv_skip.set_alignment(1, 0.5)
table.attach(label_import_csv_skip, 2, 3, 1, 2)
self.import_csv_skip_adj = gtk.Adjustment(0, 0, 100, 1, 10)
@@ -266,7 +266,6 @@ class WinImport(object):
if word not in self.fields_invert and word not in self.fields:
iter = self.model1.get_iter_first()
prefix = ''
prefix_name = ''
for parent in word.split('/')[:-1]:
while iter:
if self.model1.get_value(iter, 0) == parent or \
@@ -300,7 +299,7 @@ class WinImport(object):
def _sig_sel_add(self, store, path, iter):
num = self.model2.append()
name = self.fields[store.get_value(iter, 1)][0]
self.model2.set(num, 0, name, 1, store.get_value(iter,1))
self.model2.set(num, 0, name, 1, store.get_value(iter, 1))
def sig_unsel(self, widget=None):
store, paths = self.view2.get_selection().get_selected_rows()
+3 -2
View File
@@ -3,7 +3,6 @@
import gtk
import gettext
import tryton.common as common
import tryton.rpc as rpc
from tryton.gui.window.view_form.screen import Screen
from tryton.config import TRYTON_ICON
from tryton.gui.window.win_form import WinForm
@@ -34,7 +33,8 @@ class WinSearch(NoModal):
self.accel_group = gtk.AccelGroup()
self.win.add_accel_group(self.accel_group)
self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL)
self.but_find = self.win.add_button(gtk.STOCK_FIND, gtk.RESPONSE_APPLY)
self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
self.but_ok.add_accelerator('clicked', self.accel_group,
@@ -118,6 +118,7 @@ class WinSearch(NoModal):
elif response_id == gtk.RESPONSE_ACCEPT:
screen = Screen(self.model_name, domain=self.domain,
context=self.context, mode=['form'])
def callback(result):
if result and screen.save_current():
res = [screen.current_record.id]
-2
View File
@@ -1,7 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import tryton.rpc as rpc
from preference import *
class Window(object):
+4 -3
View File
@@ -9,7 +9,6 @@ import tryton.common as common
from tryton.gui.window.view_form.screen import Screen
from tryton.gui import Main
from tryton.exceptions import TrytonServerError
from tryton.config import CONFIG
from tryton.gui.window.nomodal import NoModal
from tryton.common.button import Button
_ = gettext.gettext
@@ -74,7 +73,9 @@ class Wizard(object):
ctx['active_ids'] = self.ids
ctx['active_model'] = self.model
if self.screen:
data = {self.screen_state: self.screen.get_on_change_value()}
data = {
self.screen_state: self.screen.get_on_change_value(),
}
else:
data = {}
rpcprogress = common.RPCProgress('execute', ('wizard',
@@ -230,7 +231,7 @@ class Wizard(object):
self.screen.set_cursor()
class WizardForm(Wizard,SignalEvent):
class WizardForm(Wizard, SignalEvent):
"Wizard"
def __init__(self, name=False):
+2 -1
View File
@@ -12,6 +12,7 @@ from tryton.config import get_config_dir
__all__ = ['Server', 'Client']
class IPCServer(object):
thread = None
@@ -112,7 +113,7 @@ class FIFOServer(IPCServer):
pass
def _read(self):
fifo = os.fdopen(os.open(self.filename, os.O_RDONLY|os.O_NONBLOCK))
fifo = os.fdopen(os.open(self.filename, os.O_RDONLY | os.O_NONBLOCK))
data = ''
while self.running:
try:
+11 -8
View File
@@ -29,13 +29,14 @@ class ResponseError(xmlrpclib.ResponseError):
class Fault(xmlrpclib.Fault):
def __init__(self, faultCode, faultString='', **extra):
super(Fault, self).__init__(faultCode, str(faultString), **extra)
super(Fault, self).__init__(faultCode, faultString, **extra)
self.args = faultString
class ProtocolError(xmlrpclib.ProtocolError):
pass
def object_hook(dct):
if '__class__' in dct:
if dct['__class__'] == 'datetime':
@@ -109,7 +110,7 @@ class JSONUnmarshaller(object):
class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
accept_gzip_encoding = True
encode_threshold = 1400 # common MTU
encode_threshold = 1400 # common MTU
def __init__(self, fingerprints=None, ca_certs=None):
xmlrpclib.Transport.__init__(self)
@@ -123,7 +124,8 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
return parser, target
def get_host_info(self, host):
host, extra_headers, x509 = xmlrpclib.Transport.get_host_info(self, host)
host, extra_headers, x509 = xmlrpclib.Transport.get_host_info(
self, host)
if extra_headers is None:
extra_headers = []
extra_headers.append(('Connection', 'keep-alive'))
@@ -151,11 +153,11 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
return self._connection[1]
host, extra_headers, x509 = self.get_host_info(host)
ca_certs = self.__ca_certs
ca_certs = self.__ca_certs
cert_reqs = ssl.CERT_REQUIRED if ca_certs else ssl.CERT_NONE
class HTTPSConnection(httplib.HTTPSConnection):
def connect(self):
sock = socket.create_connection((self.host, self.port),
self.timeout)
@@ -180,12 +182,13 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
peercert = sock.getpeercert(True)
except socket.error:
peercert = None
def format_hash(value):
return reduce(lambda x, y: x + y[1].upper() +
((y[0] % 2 and y[0] + 1 < len(value)) and ':' or ''),
enumerate(value), '')
((y[0] % 2 and y[0] + 1 < len(value)) and ':' or ''),
enumerate(value), '')
return format_hash(hashlib.sha1(peercert).hexdigest())
except ssl.SSLError, e:
except ssl.SSLError:
http_connection()
fingerprint = ''
+3 -1
View File
@@ -21,11 +21,13 @@ if os.path.isdir(PLUGINS_PATH):
continue
module = os.path.splitext(plugin)[0]
try:
module = imp.load_module(module, *imp.find_module(module, [PLUGINS_PATH]))
module = imp.load_module(module, *imp.find_module(module,
[PLUGINS_PATH]))
MODULES.append(module)
except ImportError, exception:
continue
def execute(datas):
result = {}
+2
View File
@@ -5,12 +5,14 @@ import gettext
_ = gettext.gettext
def translate_view(datas):
model = datas['model']
Window.create(False, 'ir.translation', res_id=False,
domain=[('model', '=', model)],
mode=['tree', 'form'])
def get_plugins(model):
return [
(_('Translate view'), translate_view),
+3
View File
@@ -5,18 +5,21 @@ import gettext
_ = gettext.gettext
def workflow_print(datas):
datas = datas.copy()
datas['nested'] = False
Action.exec_report('workflow.instance.graph', datas)
return True
def workflow_print_complex(datas):
datas = datas.copy()
datas['nested'] = True
Action.exec_report('workflow.instance.graph', datas)
return True
def get_plugins(model):
return [
(_('Print Workflow'), workflow_print),
+3 -3
View File
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import sys
try:
import simplejson as json
except ImportError:
@@ -22,7 +21,7 @@ class PYSON(object):
raise NotImplementedError
def __invert__(self):
if self.types()!= set([bool]):
if self.types() != set([bool]):
return Not(Bool(self))
else:
return Not(self)
@@ -307,7 +306,8 @@ class If(PYSON):
def __init__(self, condition, then_statement, else_statement=None):
super(If, self).__init__()
if isinstance(condition, PYSON):
assert condition.types() == set([bool]), 'condition must be boolean'
assert condition.types() == set([bool]), \
'condition must be boolean'
else:
assert isinstance(condition, bool), 'condition must be boolean'
if isinstance(then_statement, PYSON):
+15 -5
View File
@@ -30,7 +30,9 @@ if not os.path.isfile(_CA_CERTS):
_CA_CERTS = None
_FINGERPRINTS = Fingerprints()
ServerProxy = partial(ServerProxy, fingerprints=_FINGERPRINTS, ca_certs=_CA_CERTS)
ServerProxy = partial(ServerProxy, fingerprints=_FINGERPRINTS,
ca_certs=_CA_CERTS)
def db_list(host, port):
try:
@@ -46,6 +48,7 @@ def db_list(host, port):
logging.getLogger('rpc.result').debug(repr(None))
return None
def db_exec(host, port, method, *args):
connection = ServerProxy(host, port)
logging.getLogger('rpc.request').info('common.db.%s(None, None, %s)' %
@@ -54,6 +57,7 @@ def db_exec(host, port, method, *args):
logging.getLogger('rpc.result').debug(repr(result))
return result
def server_version(host, port):
try:
connection = ServerProxy(host, port)
@@ -65,9 +69,10 @@ def server_version(host, port):
except (Fault, socket.error):
raise
def login(username, password, host, port, database):
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE, _VIEW_CACHE
global _TOOLBAR_CACHE
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE
global _VIEW_CACHE, _TOOLBAR_CACHE
_VIEW_CACHE = {}
_TOOLBAR_CACHE = {}
try:
@@ -99,9 +104,10 @@ def login(username, password, host, port, database):
IPCServer(host, port, database).run()
return 1
def logout():
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE, _VIEW_CACHE
global _TOOLBAR_CACHE
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE
global _VIEW_CACHE, _TOOLBAR_CACHE
if IPCServer.instance:
IPCServer.instance.stop()
if CONNECTION is not None:
@@ -125,6 +131,7 @@ def logout():
_VIEW_CACHE = {}
_TOOLBAR_CACHE = {}
def context_reload():
global CONTEXT, TIMEZONE, _HOST, _PORT
try:
@@ -142,6 +149,7 @@ def context_reload():
except Fault:
pass
def _execute(blocking, *args):
global CONNECTION, _USER, _SESSION
if CONNECTION is None:
@@ -186,8 +194,10 @@ def _execute(blocking, *args):
logging.getLogger('rpc.result').debug(repr(result))
return result
def execute(*args):
return _execute(True, *args)
def execute_nonblocking(*args):
return _execute(False, *args)
+3 -1
View File
@@ -5,7 +5,7 @@ import os
import locale
import gettext
from version import PACKAGE
from tryton.client import CURRENT_DIR, PREFIX
from tryton.config import CURRENT_DIR, PREFIX
import logging
import gtk
import sys
@@ -197,12 +197,14 @@ def setlang(lang=None, locale_dict=None):
conv[field] = locale_dict[field]
locale.localeconv = lambda: conv
def set_language_direction(direction):
if direction == 'rtl':
gtk.widget_set_default_direction(gtk.TEXT_DIR_RTL)
else:
gtk.widget_set_default_direction(gtk.TEXT_DIR_LTR)
def date_format():
'''
Return the locale date format
-1
View File
@@ -4,4 +4,3 @@ PACKAGE = "tryton"
VERSION = "2.3.0"
LICENSE = "GPL-3"
WEBSITE = "http://www.tryton.org/"