Mercurial > pyarq-presupuestos
diff Gtk/gui.py @ 1:2ac1551ad2ab version 0.0.0
add code
author | Miguel Ángel Bárcena Rodríguez <miguelangel@obraencurso.es> |
---|---|
date | Sun, 31 Oct 2010 20:07:33 +0100 |
parents | |
children | a221c14c3c31 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Gtk/gui.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,4509 @@ +# -*- coding: utf-8 -*- +## File gui.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## <miguelangel@obraencurso.es> +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## + +"""Gui module + +The MainWindow class contain the toplevel WINDOW, +this window have a notebook with a page for each budget. +Each budget or notebook page is showed by the Page class, this class contain +the main widget showed in a page notebook. +The main widget can show the budget information in several panes. +This panes are ordened in gtk.Paned represented for the class Paned which can +have 2 viewes represented for the View class or other gtk.Paned that have other +viewes or more gtk.Paned. +The view can have diferente type of widgets to show the budget information. +The DecompositionList class show the decompositon list information of a record +The Measure class show de measure information of a record +The TextWindow class show the long description of a record +The Sheet class class show the sheet of condition information of a record + +The views can send signal to the others. +All the viewes ordered in panes can be or not be connected to the others, +if there are connecteded to the others when the user change the active code in +one of the panes the active code change in the others. + +""" +# TODO: Config file + +# Standar Modules +import os +import time +import pygtk +pygtk.require('2.0') +import gtk +import gobject +import weakref + +# pyArq-Presupuestos Modules +from Gtk import importFiebdc +from Generic import base +from Generic import fiebdc +from Generic import durusdatabase +from Generic import utils +from Generic import globals +from Generic import openwith + +# Load default icon +if os.path.exists(globals.getAppPath("ICON")): + icon = gtk.gdk.pixbuf_new_from_file(globals.getAppPath("ICON")) + gtk.window_set_default_icon_list(icon) +else: + print utils.mapping(_("The icon file does not exist. '$1'"), + (globals.getAppPath("ICON"),)) + +# Autodetect desktop +if globals.desktop["autodetect"] is True: + openwith.autodetect_desktop() + print utils.mapping(_("pyArq-Presupuestos running on $1"), + (globals.desktop["desktop"],)) + +class MainWindow(object): + """gui.MainWindow: + + Description: + Creates and shows the main window. + This is the interface base class. + Constructor: + gui.MainWindow(): Returns the newly created main window instance + Ancestry: + +-- object + +-- MainWindow + Atributes: + "window": Main window widget ("gtk.Window" object) + "__budget_temp_list": Temporal list of budgets + "__budget_list": List of budgets ("base.Budget" objects) + "__page_list": List of pages ("Page" object) + "__notebook": Notebook widget ("gtk.Notebook" object) + "__general_action_group": the "General" action group + Methods: + __init__(self) + _main(self) + _addBudget(self, budget) + _appendPage(self) + _testBudgetList(self) + _menuitemImportFiebdc(self, widget) + _menuitemImportPriceDatabase(self, widget) + _menuitemOpenPriceDatabase(self, widget) + _menuitemOpen + _menuitemClose(self, widget) + _menuitemText(self, widget) + _delete_event(self, widget, event) + _destroy(self, widget) + """ + # TODO:* Can choose open budget in new window + # TODO:* gtk.Action for menu and toolbar + # TODO:* Can choose show more than one notebook in the same window or + # TODO: can show basedata notebook in a side pane + __ui = '''<ui> + <menubar name="MenuBar"> + <menu action="File"> + <menuitem action="ImportFiebdc"/> + <menuitem action="Close"/> + </menu> + <menu action="View"> + <menuitem action="Text"/> + </menu> + <menu action="Test"> + <menuitem action="ImportFiebdcPriceDatabase"/> + <menuitem action="OpenPriceDatabase"/> + </menu> + </menubar> + <toolbar name="ToolBar"> + <toolitem action="ImportFiebdc"/> + <separator/> + <toolitem action="Close"/> + <separator name="sep1"/> + </toolbar> + </ui>''' + + def __init__(self): + """def __init__(self) + + Initialize the atributes "__budget_list" and "__page_list" without data. + Creates the widgets "window" and "__notebook". + """ + self.__budget_temp_list = [] + self.__budget_list = [] + self.__page_list = [] + # Main window + self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) + self.window.set_default_size(771, 570) + self.window.set_title("Presupuestos") + self.window.set_border_width(0) + self.window.connect("destroy", self._destroy) + self.window.connect("delete_event", self._delete_event) + # Vertical box + _vbox1 = gtk.VBox(False, 0) + self.window.add(_vbox1) + _vbox1.show() + #Uimanager + _uimanager = gtk.UIManager() + _accelgroup = _uimanager.get_accel_group() + self.window.add_accel_group(_accelgroup) + _general_action_group = gtk.ActionGroup("General") + self.__general_action_group = _general_action_group + _general_action_group.add_actions( + [("File", None, _("_File"), None), + ("ImportFiebdc", gtk.STOCK_OPEN, _('_Import Fiebdc'), "", 'NPI', + self._menuitemImportFiebdc), + ("Close", gtk.STOCK_CLOSE, _("_Close"), None, 'NPI', + self._menuitemClose), + ("View", None, _("_View")), + ("Text", None, _("_Text"), None, 'NPI', + self._menuitemText), + ("Test", None, _("_Test")), + ('ImportFiebdcPriceDatabase', gtk.STOCK_OPEN, + _("Import Fiebdc _price database"), "", "NPI", + self._menuitemImportPriceDatabase ), + ("OpenPriceDatabase", gtk.STOCK_OPEN, _('_Open price database'), + "", 'NPI', self._menuitemOpenPriceDatabase), + ]) + _uimanager.insert_action_group(_general_action_group, 0) + _uimanager.add_ui_from_string(self.__ui) + _menu_bar = _uimanager.get_widget("/MenuBar") + _vbox1.pack_start(_menu_bar, False, False, 0) + _toolbar = _uimanager.get_widget("/ToolBar") + _toolbar.get_settings().set_long_property("gtk-toolbar-icon-size", + gtk.ICON_SIZE_SMALL_TOOLBAR, "pyArq-Presupuestos:toolbar") + _vbox1.pack_start(_toolbar, False, False, 0) + # Notebook + self.__notebook = gtk.Notebook() + _vbox1.pack_start(self.__notebook, True, True, 0) + self.__notebook.set_tab_pos(gtk.POS_TOP) + self.__notebook.set_show_tabs(True) + self.__notebook.set_show_border(True) + self.__notebook.set_scrollable(True) + self.__notebook.show() + self._main() + + def _main(self): + """def main(self) + + Shows window and starts the GTK+ event processing loop. + """ + self.window.show() + gtk.main() + + def _addBudget(self, budget): + """def _addBudget(self, budget) + + budget: "base.Budget" object + + Appends a budget in the "__budget_list" + """ + if budget != None: + _budget = budget + if _budget in self.__budget_temp_list: + self.__budget_temp_list.remove(_budget) + self.__budget_list.append(_budget) + + def _appendPage(self): + """def _appendPage(self) + + Creates a new page (instance of "Page class") from the last budget in + __budget_list, appends this page in the "__page_list" and shows the + page widget in the notebook widget. + """ + _last_budget = self.__budget_list[-1] + _page = Page(_last_budget) + self.__notebook.append_page(_page.widget, _page.title) + self.__page_list.append(_page) + + def _testBudgetList2(self): + """def _testBudgetList2(self) + + Test if the number of budgets ("__budget_list") is greater + than the number of pages ("__page_list"), if it is greater + appendPage method is called to create a page to show the new budget + and save the budget in a durus file. + """ + if len(self.__budget_list) > len(self.__page_list): + self._appendPage() + #-# in test mode + # TODO: It must be in a thread + _last_budget = self.__budget_list[-1] + _path = globals.getHomePath("DURUS-DATABASE") + _file_whit_path_bc3 = _last_budget.filename + _filename_bc3 = _file_whit_path_bc3.split("/")[-1] + _filename = _filename_bc3.split(".")[-2] + _file = _path + _filename + ".durus" + print utils.mapping(_("Saving file: $1"), (_file,)) + _time = time.time() + _durus_file = durusdatabase.DurusFile(_file,True) + _durus_file.setBudget(_last_budget) + _durus_file.close() + print utils.mapping(_("Saving time: $1 seconds"), + (("%.2f" %(time.time()-_time) ),)) + #-# + return False + return True + + def _menuitemImportFiebdc(self, widget): + """def _menuitemImportFiebdc(self, widget) + + widget: the widget where the event is emitted from + Callback to open a budget file. + Inits a timeout to test if a budgets is appended to "__budget_list" + calling _testBudgetList method if it is true. + + Creates and shows a window to open a budget file. + """ + _budget = base.Budget() + self.__budget_temp_list.append(_budget) + _budget_file = fiebdc.Read() + _read_method = _budget_file.readFile + _filename = "file" + _exit_method = _budget_file.cancel + _file_window = importFiebdc.FileSelectionWindow(self, + _read_method, _budget, _filename, _exit_method) + + def _menuitemImportPriceDatabase(self, widget): + """def _menuitemImportPriceDatabase(self, widget) + + widget: the widget where the event is emitted from + Callback to open a budget file. + Inits a timeout to test if a budgets is appended to "__budget_list" + calling _testBudgetList method if it is true. + + Creates and shows a window to open a budget file. + """ + #TODO: change timeout, it is deprecated + #TODO: the .durus file must be direted saved + gobject.timeout_add(100, self._testBudgetList2) + _budget = base.Budget() + self.__budget_temp_list.append(_budget) + _budget_file = fiebdc.Read() + _read_method = _budget_file.readFile + _filename = "file" + _exit_method = _budget_file.cancel + _file_window = importFiebdc.FileSelectionWindow(self, + _read_method, _budget, _filename, _exit_method) + + def _menuitemOpenPriceDatabase(self, widget): + """def _menuitemImportPriceDatabase(self, widget) + + widget: the widget where the event is emitted from + Callback to open a budget file. + Inits a timeout to test if a budgets is appended to "__budget_list" + calling _testBudgetList method if it is true. + + Creates and shows a window to open a budget file. + """ + _openDialog = OpenDurusDatabase(self._OpenDurusDatabase) + _openDialog.main() + + def _OpenDurusDatabase(self, file): + """def _menuitemImportFiebdc(self, widget) + + widget: the widget where the event is emitted from + + Callback to open a budget file from a durus file. + """ + _file = file + print utils.mapping(_("Loading file: $1:"), (_file,)) + _time = time.time() + _durus_file = durusdatabase.DurusFile(_file,False) + _budget = _durus_file.getBudget() + _durus_file.close() + print utils.mapping(("Loadig time: $1 seconds"), + (("%.2f" %(time.time()-_time)),)) + self.__budget_list.append(_budget) + _page = Page(_budget) + self.__notebook.append_page(_page.widget, _page.title) + self.__page_list.append(_page) + + def _menuitemClose(self, widget): + """def _menuitemClose(self, widget) + + widget: the widget where the event is emitted from + + Callback to close a budget file. + """ + _page_num = self.__notebook.get_current_page() + if _page_num == -1: + return + _page = self.__page_list.pop(_page_num) + if isinstance(_page, Page): + #not loading budget + self.__budget_list.pop(_page_num) + _page.clear() + self.__notebook.remove_page(_page_num) + + def _menuitemText(self, widget): + """_menuitemText(self, widget) + + widget: the widget where the event is emitted from + + Creates and shows a window showing text description + using a instance of TextWindow class. + """ + _page_num = self.__notebook.get_current_page() + if _page_num == -1: + return + _budget = self.__budget_list[_page_num] + _page = self.__page_list[_page_num] + _code = _page.getActiveCode() + _concepto = _budget.getRecord(_code) + _text = _concepto.text + _window = TextWindow(_code, _text) + _window.main() + + def _delete_event(self, widget, event): + """_delete_event(self, widget, event) + + widget: the widget where the event is emitted from + event: the "gtk.gdk.Event" + + Method connected to "delete_event" signal of main window widget + This signal is emitted when a user press the close titlebar button. + It Returns True so the signal "destroy" is emitted. + """ + for _page in self.__page_list: + _page.clear() + return False # -> destroy + + def _destroy(self, widget): + """_destroy(self, widget) + + widget: the widget where the event is emitted from + Method connected to "destroy" signal of main window widget + + This signal is emited when the method connected to "delete_event" + signal returns True or when the program call the destroy() method of + the gtk.Window widget. + The window is closed and the GTK+ event processing loop is ended. + """ + gtk.main_quit() + + def getNotebook(self): + return self.__notebook + def getPageList(self): + return self.__page_list + def getBudgetList(self): + return self.__budget_list + +class EmptyPage(object): + """ + """ + def __init__(self, mainWindow, readFileMethod, budget, filename, + cancelMethod): + """def __init__(self, mainWindow, readFileMethod, budget, filename, + cancelMethod) + + """ + self.__mainWindow = mainWindow + self.__readFileMethod = readFileMethod + self.__budget = budget + self.__filename = filename + self.__cancelMethod = cancelMethod + self.__children = None + self.__cancel = [False, False] + self.__progress = 0.0 + self.__widget = gtk.VBox() + self.__main_item = None + self.__widget.show() + self.__throbber = gtk.Image() + self.__throbber.set_from_file(globals.getAppPath("THROBBER-ICON")) + self.__throbber.show() + self.__animationThobber = gtk.gdk.PixbufAnimation( + globals.getAppPath("THROBBER-GIF")) + self.__quietThobber = self.__throbber.get_pixbuf() + self.__budget_icon = gtk.gdk.pixbuf_new_from_file_at_size( + globals.getAppPath("BUDGET-ICON"), 16, 16) + _filename = os.path.basename(filename) + _rootfilename = os.path.splitext(_filename)[0] + if not _rootfilename == "": + _filename = _rootfilename + _titleLabel = gtk.Label(_filename) + _titleLabel.show() + self.__title = gtk.HBox() + self.__title.add(self.__throbber) + self.__title.add(_titleLabel) + self.__statusbar = gtk.Statusbar() + self.__statuscontext = self.__statusbar.get_context_id("Statusbar") + self.__statusbar.show() + _align = gtk.Alignment(0.5, 0.5, 0, 0) + _iconVbox = gtk.VBox() + _pyArqIcon = gtk.Image() + _pyArqIcon.set_from_file(globals.getAppPath("PYARQ-ICON")) + _pyArqIcon.show() + _iconVbox.pack_start(_pyArqIcon, True, True, 0) + _link = gtk.LinkButton("http://pyarq.obraencurso.es", + "http://pyarq.obraencurso.es") + _iconVbox.pack_start(_link, True, True, 0) + _link.show() + _iconVbox.show() + _align.add(_iconVbox) + _align.show() + self.__widget.pack_start(_align, True, True, 0) + _progressframe = gtk.Frame() + _progressframe.set_shadow_type(gtk.SHADOW_IN) + _progressframe.show() + self.__progress_bar = gtk.ProgressBar() + self.__progress_bar.show() + _progressframe.add(self.__progress_bar) + self.__statusbar.pack_start(_progressframe, False, False, 0) + self.__widget.pack_end(self.__statusbar, False, True, 0) + self.__main_item = None + + def run(self): + self.__statusbar.push(self.__statuscontext, _("Time: 0s")) + self.__throbber.set_from_animation(self.__animationThobber) + self._launchChildren() + self._launchTimeout() + + def progress(self, percent): + _progress = str(int(round(100 * percent,0))) + self.__progress = percent + + def stopLoading(self): + self.__throbber.set_from_pixbuf(self.__budget_icon) + self.__progress_bar.hide() + self.__statusbar.pop(self.__statuscontext) + + def _launchChildren(self): + """_launchChildren(self) + + Launch the thread to read the file + """ + if self.__children is None: + self.__children = importFiebdc.Thread(self, self.__mainWindow, + self.__readFileMethod, self.__budget, self.__filename, + self.__cancelMethod) + self.__children.start() + + def _launchTimeout(self): + """def _launchTimeout(self) + + Launch the timeouts: + 1- update progress bar + 2- update time label + 3- If the other timetouts are stoped the window is closed + """ + gobject.timeout_add(500, self._updateProgressBar) + gobject.timeout_add(1000, self._updateLabel, time.time()) + self.__cancel = [False, False] + gobject.timeout_add(1000, self._autoClose) + + def _updateProgressBar(self): + """def _updateProgressBar(self) + + update progress bar in a timeout + If the thread end or is canceled the timeout is stoped + """ + if self.__children is None or self.__children.isCanceled() == True: + self.__cancel[0] = True + return False + else: + self.__progress_bar.set_fraction(self.__progress) + _text = "%s%%" %str(int(round(100 * self.__progress,0))) + self.__progress_bar.set_text(_text) + return True + + def _updateLabel(self, _time): + """def _updateProgressBar(self) + + update time label in a timeout + If the thread end or is canceled the timeout is stoped + """ + if self.__children is None or self.__children.isCanceled() == True: + self.__cancel[1] = True + return False + else: + _time = time.time() - _time + _text = utils.mapping(_("Time: $1"), ("%.0f" %_time,)) + self.__statusbar.pop(self.__statuscontext) + self.__statusbar.push(self.__statuscontext, _text) + return True + + def _autoClose(self): + """def _updateProgressBar(self) + + If the time label and progress bar timeouts are stoped the window is + closed and ist tiemeout is stoped + """ + if self.__cancel == [ True, True ]: + return False + else: + return True + + def closeWindow(self): + """def closeWindow(self) + + Sets the __children atribute to None + This causes that the timeouts is ended. + This method is called from thread when it is finished + TODO: it must called threadFinished or somethig + """ + self.__children = None + self.stopLoading() + _page = Page(self.__budget) + _children = self.__widget.get_children() + for _child in _children: + self.__widget.remove(_child) + self.__widget.pack_start(_page.widget, True, True, 0) + _noteBook = self.__mainWindow.getNotebook() + _pageIndex = _noteBook.page_num(self.__widget) + self.__mainWindow.getPageList()[_pageIndex] = _page + def threadCanceled(self): + """def threadCanceled(self) + + Sets the __children atribute to None + This causes that the timeouts is ended. + This method is called from thread when is canceled + TODO: it must called threadFinished or somethig + """ + self.__children = None + self.stopLoading() + + def clear(self): + """def clear(self) + + Cancel thread + """ + self.__children.cancel() + + def getWidget(self): + """def getWidget(self) + + Return de main widget to show in the page + """ + return self.__widget + + def getTitle(self): + """def getTtle(self) + + Return the title of the page, a gtk.Label objetc + """ + return self.__title + + widget = property(getWidget, None, None, + "Main widget showed in the pane") + title = property(getTitle, None, None, + "Page Title") + +class Page(object): + """gui.Page: + + Description: + It creates and shows a page in the notebook from a budget object. + The page can show the budget information in several panes ordered + according to "panes_list" information. + Constructor: + gui.Page(budget, active_code=None): + budget: budget to be showed in this page (base.Budget object) + active_code: the code of the active record + Returns the newly created Page instance + Ancestry: + +-- object + +-- Page + Atributes: + "budget": Read-Write. Budget to show in the page. (base.obra object) + "panes_list": Read. info list for create the panes + ej: [ "v", pane1, pane2 ] , [ "h", pane1, pane2 ] + [ "v", [ "h", pane1, pane2 ], [ "h", pane1, pane2 ] ] + pane types: + * "DecompositionList": its creates a "DecompositionList" object + * "RecordDescription" : its creates a "Description" objetc + * "Measure": its creates a "Measure" objetc + * "FileView": its creates a "FileView" objet + * "CompanyView": its creates a "CompanyView" object + "widget": Read. Notebook page Widget. (a gtk.VBox instance) + "title": Read. Notebook page title (gtk.Label object) + "__active_path_record": The active path record + "__main_item": main item in the page, can be a View object or a Paned + object + Methods: + __init__(self, budget=None, active_code=None) + propagateMessageFrom(self, message, path, arg=None) + sendMessageTo(self, pane, message, path, arg=None) + clear(self) + getItem(self,path) + setMainItem(self, item) + itemsFactory(self, list_paned, path=(0,)) + setActivePathRecord(self, path_record) + getTitle(self) + getWidget(self) + setBudget(self, budget) + getBudget(self) + getPanesList(self) + """ + # TODO: * The panes can be ordered as the user wishes + # TODO: * Panes in windows + # TODO: * pane types + # TODO: * General budget properties (is better a dialog?) + + def __init__(self, budget, path_record=(0,)): + """def __init__(self, budget=None, active_code=None) + + budget: "base.Budget" object + active_code: the code of the active record + Sets the atributes + * __panes_list: info to create the panes + * budget (base.Budget object) + * active_code + """ + #TODO: __panes_list should come from config file... + self.__widget = gtk.VBox() + self.__panes_list = [ "v", "DecompositionList", [ "v", "Measure", + "RecordDescription" ]] + self.__main_item = None + self.setBudget(budget) + self.setActivePathRecord(path_record) + self.__widget.show() + + def propagateMessageFrom(self, message, path, arg=None): + """def propagateMessageFrom(self, message, path, arg=None) + + message: string message + path: tuple that represents the pane path which emits the message + arg: arguments for the message + if message is "change_active" arg is the path record + + The panes are connectted to this method to send messages to other panes + """ + _budget = self.__budget + if message == "change_active" and _budget.hasPath(arg): + self.sendMessageTo(self.__main_item, message, path, arg) + elif message == "autoclose": + self._closeItem(path) + elif message == "split h": + self._splitItem(path, "h") + elif message == "split v": + self._splitItem(path, "v") + + def sendMessageTo(self, pane, message, path, arg=None): + """def sendMessageTo(self, pane,message, path, arg=None) + pane: the receiver pane + message: string message + path: tuple that represents the pane path which emits the message + arg: arguments for the message + + Sends a message to a pane + """ + if not pane.path == path: + pane.runMessage(message, path, arg) + + def clear(self): + """def clear(self) + + Clear atributes + """ + self.propagateMessageFrom("clear", (0,)) + + del self.__budget + del self.__panes_list + del self.__widget + del self.__title + del self.__active_path_record + del self.__main_item + + def getItem(self,path): + """def getItem(self, path + + Return the item whith the path "path", it can return a Paned instance + or a View instance + """ + _item = self.__main_item + if len(path) == 1: + return _item + else: + return _item.getItem(path[1:]) + + def setMainItem(self, item): + """setMainItem(self,item) + + Sets a new main item in the page + """ + if not self.__main_item is None: + _old_main_widget = self.__main_item.widget + self.__widget.remove(_old_main_widget) + self.__main_item = item + _main_widget = self.__main_item.widget + _main_widget.show() + self.__widget.pack_start(_main_widget, True, True, 0) + + def _splitItem(self, path, orientation): + """_splitItem(self, path, orientation) + + Splits the item that is identifies by the path and the orientation + """ + _item = self.getItem(path) + _parent = self.getItem(path[:-1]) + _item.setPath(path+ (0,)) + _item_clone0 = _item.getClone(path + (0,)) + _item_clone1 = _item.getClone(path + (1,)) + _paned = Paned(orientation, path, _item_clone0, _item_clone1) + if len(path) > 1: + _parent.setItem(path[-1], [_paned]) + else: + self.setMainItem(_paned) + + def _closeItem(self, path): + """_closeItem(self, path) + + Closes the item that is identifies by the path + """ + _item = self.getItem(path) + if len(path) > 1: + # There are more than one item + _parent = self.getItem(path[:-1]) + _brothers = [ _brother for _brother in _parent] + _brothers.remove(_item) + _brother = _brothers[0] + + _parent.widget.remove(_brother.widget) + _brother.path = path[:-1] + if len(path) > 2: + _grandparent = self.getItem(path[:-2]) + _grandparent.setItem(path[-2], [_brother]) + _parent.widget.destroy() + _parent.clear() + _item.clear() + else: + _grandparent = self + _grandparent.setMainItem(_brother) + _parent.widget.destroy() + _parent.clear() + _item.clear() + else: + # Thre is only one item in the page, it can not be closed + pass + + def itemsFactory(self, list_paned, path=(0,)): + """def itemsFactory(self, list_paned, path(0,)) + + list_paned: list in "__panes_list" format + [ "v" or "h", panel1_type, panel2_type] + which contains the info for create the widgets. + panel types: + * "DecompositionList" + * "RecordDescription" + * "Measure" + * "Sheet of Conditions" + * "FileView" + * "CompanyView" + path: tuple that represents the item path in the page + + Creates the items and widgets and returns the main item + """ + if not isinstance(list_paned , list): + raise ValueError, _("The value must be a list") + if list_paned[0] == "v" or list_paned[0] == "h": + if len(list_paned) != 3: + raise ValueError, _("Incorrect len") + if not isinstance(list_paned[1],list): + list_paned[1] = [list_paned[1]] + if not isinstance(list_paned[2],list): + list_paned[2] = [list_paned[2]] + _item1 = self.itemsFactory(list_paned[1],path + (0,)) + _item2 = self.itemsFactory(list_paned[2],path + (1,)) + _item = Paned(list_paned[0], path, _item1, _item2) + elif list_paned[0] == "DecompositionList": + _item = View( "DecompositionList", self.__budget, + weakref.ref(self), path, self.__active_path_record) + elif list_paned[0] == "RecordDescription": + _item = View( "RecordDescription", self.__budget,weakref.ref(self), + path, self.__active_path_record) + elif list_paned[0] == "Measure": + _item = View( "Measure", self.__budget, weakref.ref(self), path, + self.__active_path_record) + elif list_paned[0] == "Sheet of Conditions": + _item = Sheet(sef.__budget, weakref.ref(self), path, + self.__active_path_record) + elif list_paned[0] == "FileView": + _item = FileView(sef.__budget, weakref.ref(self), path, + self.__active_path_record) + elif list_paned[0] == "CompanyView": + _item = CompanyView(sef.__budget, weakref.ref(self), path, + self.__active_path_record) + else: + _item = None + raise ValueError, utils.mapping(_("Incorrect item $1"), + (str(list_paned[0]),)) + return _item + + def setActivePathRecord(self, path_record): + """def setActivePathRecord(self, path_record) + + path_record: the active record path + + Sets the active record path + """ + if self.__budget.hasPath(path_record): + self.__active_path_record = path_record + else: + raise ValueError, utils.mapping(_("The budget does not have the "\ + "path record: $1"), (str(path_record),)) + + def getTitle(self): + """def getTtle(self) + + Return the title of the page, a gtk.Label objetc + """ + return self.__title + + def getWidget(self): + """def getWidget(self) + + Return de main widget to show in the pane + """ + return self.__widget + + def setBudget(self, budget): + """def setBudget(self, budget) + + budget: a base.Budget object + + Sets the budget and the active code atributes, + creates the page title and the widgets in the pane and + shows the main widget. + """ + if budget is None: + self.clear() + return + self.__budget = budget + self.setActivePathRecord((0,)) + ## Todo: change page title + self.__title = gtk.Label(self.__budget.getCode( + self.__active_path_record)) + _panes_list = self.__panes_list + self.__main_item = self.itemsFactory(_panes_list) + _main_widget = self.__main_item.getWidget() + _main_widget.show() + self.__widget.pack_start(_main_widget, True, True, 0) + + def getBudget(self): + """def getBudget(self) + + Return de budget, a "base.Budget" object. + """ + return self.__budget + + def getPanesList(self): + """def getPanesList(self) + + Return the panes list, info list for create the panes. + """ + return self.__panes_list + + budget = property(getBudget, setBudget, None, + "Budget to show, base.Budget object") + widget = property(getWidget, None, None, + "Main widget showed in the pane") + title = property(getTitle, None, None, + "Page Title") + panes_list = property(getPanesList, None, None, + "Info list for create the panes") + +class View(object): + """gui.View: + + Description: + It creates a view to show the budget info + Constructor: + View(view_type, budget, wr_page, path, active_path_record) + Ancestry: + +-- object + +-- Paned + Atributes: + "path": the tuple that identifies the view in the main notebook page + "widget": the main gtk widget to show in a view object, + a gtk.VBox object + "__view_type": the object type to show + * DecompositionList + * Description + * Measure + * Sheet of conditions + * FileView + * CompanyView + "__wr_page": weak reference to the page where the view must be showed + "__budget": the budget to show + "__view ": the object to show: + * DecompositionList object + * Description object + * Measure object + * Sheet object + * FileView object + * Comapany View + "__connected": boolean value, True means that the View object sends and + receives signals from/to others views + "__connected_button": a button to switch __connected True or False + Methods: + __init__(self) + getItem(self, path) + _closeItem(self, close_button) + _change_combo(self, combobox) + propagateMessgeFrom(self, message, path, arg=None) + runMessage(self, message, path, arg=None) + getWidget(self) + getPath(self) + setPath(self) + getClone(self, newpath) + clear(self) + """ + def __init__(self, view_type, budget, wr_page, path, active_path_record): + + """def __init__(self, view_type, budget, wr_page, path, + active_path_record) + view_type: the object type to show + * DecompositionList + * Description + * Measure + * Sheet + * FileView + * CompanyView + budget: the budget to show + wr_page: weak reference to the page where the view must be showed + path: the position or path of the view in the page notebook + active_path_record: the record path that must be showed + + Creates and shows a new view + """ + self.__active_path_record = active_path_record + self.__view_type = view_type + self.__wr_page = wr_page + self.__budget = budget + self.__path = path + self.__connected = True + # view_type liststore + _liststore = gtk.ListStore(str) + _liststore.append([_("Decomposition")]) #0 + _liststore.append([_("Description")]) #1 + _liststore.append([_("Measure")]) #2 + _liststore.append([_("Sheet of Conditions")]) #3 + _liststore.append([_("Files")]) #4 + _liststore.append([_("Companies")]) #5 + _combobox = gtk.ComboBox(_liststore) + _cell = gtk.CellRendererText() + _combobox.pack_start(_cell, True) + _combobox.add_attribute(_cell, 'text', 0) + _vbox = gtk.VBox() + _vbox.show() + _toolitem = gtk.ToolItem() + _toolitem.set_expand(True) + _toolitem.add(_vbox) + _toolitem.show() + self.__widget = gtk.Toolbar() + self.__widget.insert(_toolitem, 0) + _hbox = gtk.HBox() + if view_type == "DecompositionList": + self.__view = DecompositionList(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(0) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("DECOMPOSITION-ICON")) + elif view_type == "RecordDescription": + self.__view = Description(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(1) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("DESCRIPTION-ICON")) + elif view_type == "Measure": + self.__view = Measure(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(2) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("MEASURE-ICON")) + elif view_type == "Sheet of Conditions": + self.__view = Sheet(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(3) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + elif view_type == "FileView": + self.__view = FileView(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(4) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + elif view_type == "CompanyView": + self.__view = CompanyView(budget, weakref.ref(self), path, + active_path_record) + _combobox.set_active(5) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + + else: + raise ValueError, _(utils.mapping("Invalid type of View: $1", + view_type)) + _view_icon.show() + _combobox.connect("changed", self._change_combo) + _combobox.show() + _vbox.pack_start(_hbox,False) + _vbox.pack_start(self.__view.widget, True, True) + _hbox.pack_start(_view_icon, False, False,0) + _hbox.pack_start(_combobox, False, False,0) + _invisible = gtk.HBox() + _invisible.show() + _hbox.pack_start(_invisible, True, False,0) + _icon_menu = gtk.Image() + _icon_menu.set_from_file(globals.getAppPath("MENU-ICON")) + _icon_menu.show() + _menu_button = gtk.ToolButton() + _menu_button.set_icon_widget(_icon_menu) + _menu_button.connect("clicked", self._menu_view) + _menu_button.show() + _icon_connected = gtk.Image() + _icon_connected.set_from_file(globals.getAppPath("CONNECTED-ICON")) + _icon_connected.show() + _hbox.pack_start(_menu_button, False, False, 0) + self.__connected_button = gtk.ToolButton() + self.__connected_button.set_icon_widget(_icon_connected) + self.__connected_button.connect("clicked", self._connected) + self.__connected_button.show() + _hbox.pack_start(self.__connected_button, False, False, 0) + _icon_close = gtk.Image() + _icon_close.set_from_file(globals.getAppPath("CLOSE-ICON")) + _icon_close.show() + _close_button = gtk.ToolButton() + _close_button.set_icon_widget(_icon_close) + _close_button.connect("clicked", self._closeItem) + _close_button.show() + _hbox.pack_start(_close_button, False, False, 0) + _hbox.show() + self.__widget.show() + + def getItem(self, path): + """def getItem(self, path) + + Return itself. + """ + return self + + def _closeItem(self, close_button): + """_closeItem(self, widget) + + Method connected to the "clicked" signal of the _close_button widget + Send the "autoclose" message to the page to close this view + """ + self.propagateMessageFrom( "autoclose", self.__path) + + def _change_combo(self, combobox): + """_change_combo(self, combobox) + + Method connected to the "changed" signal of the _combobox widget + It changes the view type to the type selected in the combobox + """ + _index = combobox.get_active() + _budget = self.__view.budget + _wr_page = self.__view.page + _path = self.__view.path + _path_record = self.__view.active_path_record + _toolitem = self.__widget.get_nth_item(0) + _vbox= _toolitem.get_children()[0] + _hbox = _vbox.get_children()[0] + _combobox = _hbox.get_children()[1] + _hbox.remove(_combobox) + _invisible = _hbox.get_children()[1] + _hbox.remove(_invisible) + _menu_button = _hbox.get_children()[1] + _hbox.remove(_menu_button) + _connected_button = _hbox.get_children()[1] + _hbox.remove(_connected_button) + _close_button = _hbox.get_children()[1] + _hbox.remove(_close_button) + _vbox.remove(self.__view.widget) + _vbox.remove(_hbox) + _hbox.destroy() + _view_icon = gtk.Image() + if _index == 0: + self.__view = DecompositionList(_budget, _wr_page, _path, + _path_record) + + _view_icon.set_from_file(globals.getAppPath("DECOMPOSITION-ICON")) + self.__view_type = "DecompositionList" + elif _index == 1: + self.__view = Description(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("DESCRIPTION-ICON")) + self.__view_type = "RecordDescription" + elif _index == 2: + self.__view = Measure(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("MEASURE-ICON")) + self.__view_type = "Measure" + elif _index == 3: + self.__view = Sheet(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + self.__view_type = "Sheet of Conditions" + elif _index == 4: + self.__view = FileView(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + self.__view_type = "FileView" + elif _index == 5: + self.__view = CompanyView(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + self.__view_type = "CompanyView" + _view_icon.show() + _hbox = gtk.HBox() + _hbox.pack_start(_view_icon, False, False,0) + _hbox.pack_start(_combobox, False, False,0) + _hbox.pack_start(_invisible, True, False,0) + _hbox.pack_start(_menu_button, False, False, 0) + _hbox.pack_start(_connected_button, False, False, 0) + _hbox.pack_start(_close_button, False, False, 0) + _hbox.show() + _vbox.pack_start(_hbox, False, False, 0) + _vbox.pack_start(self.__view.widget, True, True, 0) + + def _menu_view(self, widget): + """_menu_view(self, widget) + + Method connected to the "clicked" signal of the __connected_button + It shows a popup menu with some options + """ + _menu_view = gtk.Menu() + _item_leftright = gtk.MenuItem("Split View Left/Right") + _menu_view.append(_item_leftright) + _item_leftright.connect_object("activate", self._split_view, "h") + _item_leftright.show() + _item_topbottom = gtk.MenuItem("Split View Top/Bottom") + _menu_view.append(_item_topbottom) + _item_topbottom.connect_object("activate", self._split_view, "v") + _item_topbottom.show() + _item_close = gtk.MenuItem("close view") + _menu_view.append(_item_close) + _item_close.connect_object("activate", self._closeItem, None) + _item_close.show() + _menu_view.popup(None, None, None, 0, 0) + + def _split_view(self, orientation): + """_menu_view(self, orientation) + + orientation: orientation split, "h" or "v" + + Method connected to the "activate" signal of the _item_leftright and + _item_topbottom menu items. + It sends the "split" message to the page to splits the view in the + specified orientation + """ + self.propagateMessageFrom( "split " + orientation, self.__path) + + def _connected(self, widget): + """_connected(self, widget) + + Method connected to the "clicked" signal of the _menu_button + It changes the __connected atribute to True or False, if the + _connected atribute is False the view do not send and receive messages + to/from others views + """ + if self.__connected: + _icon = gtk.Image() + _icon.set_from_file(globals.getAppPath("DISCONNECTED-ICON")) + _icon.show() + self.__connected_button.set_icon_widget(_icon) + self.__connected = False + else: + _icon = gtk.Image() + _icon.set_from_file(globals.getAppPath("CONNECTED-ICON")) + _icon.show() + self.__connected_button.set_icon_widget(_icon) + self.__connected = True + + def propagateMessageFrom(self, message, path, arg=None): + """def propagateMessageFrom(self, message, path, arg=None) + + message: string message + path: tuple that represents the pane path which emits the message + arg: arguments for the message + The panes are connectted to this method to send messages to other panes + """ + if self.__connected or message == "autoclose" or \ + message == "split h" or message == "split v": + self.__wr_page().propagateMessageFrom(message, path, arg) + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + if self.__connected: + self.__view.runMessage(message, path, arg) + if message == "change_active": + if self.__budget.hasPath(arg): + _path_record = arg + self.__active_path_record = _path_record + + def getWidget(self): + """def getWidget(self) + + Return de pane widget + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__view.path + + def setPath(self, path): + """def setPath(self) + + set the tuple that identifies the pane in the notebook page + """ + self.__path = path + self.__view.path = path + + def getClone(self, new_path): + """getClone(self, new_path) + + new_path: the path that identifies the clone view in the page + + return a clone of itself + """ + return View(self.__view_type, self.__budget, self.__wr_page, + new_path, self.__active_path_record) + + def clear(self): + """clear(self) + + Clear the intance atributes + """ + del self.__wr_page + del self.__budget + del self.__path + del self.__widget + del self.__view + del self.__connected + del self.__connected_button + + path = property(getPath, setPath, None, + "path that identifies the item in the notebook page") + widget = property(getWidget, None, None, "View widget") + +class Paned(object): + """gui.Paned: + + Description: + It creates and shows gtk.Hpaned or gtk.Vpaned to show in page budget + Constructor: + Paned(orientation, widget1, widget2) + orientation: The orientation of the pane separator, can be "v" or "h" + widget1: the top or left pane widget + widget2: the botton or right pane widget + Returns the newly created Paned instance + Ancestry: + +-- object + +-- Paned + Atributes: + "widget": Pane widget("gtk.VPaned" or "gtk.HPaned" object) + "__orientation": The orientation of de gtk.Paned, can be "v" or "h" + "__items": list of items showed in the paned, its can be View or Paned + instances + "__path": the paned path in the page + Methods: + __init__(self) + __getitem__(self, item) + getClone(self, new_path) + getItem(self, path) + runMessage(self, messge, path, arg=None) + getWidget(self) + {get/set}Path + clear(self) + """ + # TODO: *control the position paned separator. Now is always 200 pixels + # TODO: can be with a float(0.0-1.0) aspect ratio + # TODO: 0.0 no space for widget1 + # TODO: 1.0 all the space for widget1 + # TODO: *control the position pane separator when the size of the window + # TODO: change with the same ascpect ratio + + def __init__(self, orientation, path, item1, item2): + """def __init__(self, oritentation, path, item1, item2) + + orientation: The orientation of de gtk.Paned, can be "v" or "h" + path: the paned path in the page + item1: the top or left pane object + item2: the bottom or right pane object + + Creates and shows a new gtk.Paned + """ + self.__orientation = orientation + if not isinstance(item1.widget, gtk.Widget) or \ + not isinstance(item2.widget, gtk.Widget): + raise ValueError, _("The item must be a widget object.") + if orientation == "v": + self.__widget = gtk.VPaned() + elif orientation == "h": + self.__widget = gtk.HPaned() + else: + raise ValueError, _("Invalid orientation.") + self.__widget.pack1(item1.widget,True,False) + self.__widget.pack2(item2.widget,True,False) + self.__widget.set_position(200) + self.__widget.show() + self.__items = [item1, item2] + self.__path = path + + def __getitem__(self, item): + """__getitem__(self, item) + + Called to implement evaluation of self[key]. + The accepted keys should be integers 0 or 1. + """ + return self.__items[item] + + def getClone(self, new_path): + """getClone(self, new_path) + + Return a clone Paned instance with the path new_path + """ + return Paned(self.__orientation, new_path, + self.__items[0].getClone(new_path + (0,)), + self.__items[1].getClone(new_path + (1,))) + + def getItem(self,path): + """def getItem(self, path) + + Return the item whith the specified path. + """ + _item = self.__items[path[0]] + if len(path) == 1: + return _item + else: + return _item.getItem(path[1:]) + + def setItem(self, path, item_list): + """def setItem(self, path, item_list) + + Sets the first item in the item_list whith the especified path and + remove the old item in this position. + """ + item = item_list[0] + if path == 0 or path == 1: + _old_item = self.__items[path] + self.__widget.remove(_old_item.widget) + self.__items[path] = item + if path == 0: + self.__widget.pack1(item.widget,True,False) + else: + self.__widget.pack2(item.widget,True,False) + return True + return False + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, page_path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + page_path: tuple that identifies the pane in the notebook page + arg: arguments + + This method receives a message and send this to the items of the paned + """ + for _item in self.__items: + if not _item.path == path: + _item.runMessage(message, path, arg) + + def getWidget(self): + """def getWidget(self) + + Return de gtk.Paned widget + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + Return de Paned path in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + self.__items[0].path = path + (0,) + self.__items[1].path = path + (1,) + + def clear(self): + del self.__widget + del self.__orientation + del self.__items + del self.__path + + widget = property(getWidget, None, None, "gtk.Paned widget") + path = property(getPath, setPath, None, "Pane path in the notebook page") + +class TreeView(object): + """gui.Treeviev: + + Description: + It creates the columns in a treeview, is the base class for + DescompositionList and Measure classes + Constructor: + TreView(args) + args: list of tuples, the tuple items are: + 0.type: + * index column + * float column + * text column + * calculated column + * calculated text + * type column + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + Ancestry: + +-- object + +-- TreeView + Atributes: + "columns": list of columns (gtk.TreeViewColumn isntances) + Methods: + __init__(self) + __getitem__(self, item) + createColumn(self, args) + createTextBaseColumn(self,args) + createBaseColumn(self,args) + """ + + def __init__(self, args): + """__init__(self, args) + + args: list of tuples, the tuple items are: + 0.type: + * index column + * float column + * text column + * calculated column + * Calculated text + * type column + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + + Create the columns form the args info calling creatheColumn to create + each column + """ + self.columns = [ self.createColumn(arg) for arg in args ] + self.columns.append(self.createColumn(("END",))) + + def createColumn(self, args): + """createColumn(self, args) + + args: tuple with the args + 0.type: + * index column + * float column + * text column + * calculated column + * calculated text + * type column + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + + Return a column created whith the arg info + """ + if args[0] == "INDEX": + _index_column = self.createBaseColumn(args) + _text_index_cell = gtk.CellRendererText() + _text_index_cell.set_property('foreground-gdk', + gtk.gdk.color_parse(globals.color["TEXT"])) + _pixbuf_index_cell = gtk.CellRendererPixbuf() + _arrow_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("ARROW-ICON")) + _pixbuf_index_cell.set_property("pixbuf", _arrow_icon) + _index_column.pack_start(_text_index_cell, True) + _index_column.pack_start(_pixbuf_index_cell, True) + _index_column.set_cell_data_func(_text_index_cell, + self.colorCell, + [gtk.gdk.color_parse(globals.color["INDEX-UNEVEN"]), + gtk.gdk.color_parse(globals.color["INDEX-EVEN"])]) + return _index_column + elif args[0] == "TEXT": + _column, _cell = self.createTextBaseColumn(args) + _column.add_attribute(_cell, 'text', args[5]) + return _column + elif args[0] == "FLOAT": + _column, _cell = self.createTextBaseColumn(args) + _column.add_attribute(_cell, 'text', args[5]) + _column.get_cell_renderers()[0].set_property('xalign', 1.0) + return _column + elif args[0] == "CALCULATED": + _column, cell = self.createTextBaseColumn(args) + _column.get_cell_renderers()[0].set_property('xalign', 1.0) + return _column + elif args[0] == "CALCULATEDTEXT": + _column, cell = self.createTextBaseColumn(args) + return _column + elif args[0] == "TYPE": + _column = self.createBaseColumn(args) + _type_cell1 = gtk.CellRendererPixbuf() + _type_cell2 = gtk.CellRendererText() + _type_cell2.set_property('foreground-gdk', args[3]) + _column.pack_start(_type_cell1, True) + _column.pack_start(_type_cell2, True) + _column.add_attribute(_type_cell2, 'text', args[5]) + _column.set_cell_data_func(_type_cell1, + self.colorCell, args[4]) + _column.set_cell_data_func(_type_cell2, + self.colorCell, args[4]) + return _column + elif args[0] == "PIXBUF": + _column = self.createBaseColumn(args) + _type_cell1 = gtk.CellRendererPixbuf() + _column.pack_start(_type_cell1, True) + _column.set_cell_data_func(_type_cell1, + self.colorCell, args[4]) + return _column + elif args[0] == "END": + _end_column = gtk.TreeViewColumn() + _end_column.set_clickable(False) + _end_cell = gtk.CellRendererText() + _end_cell.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["UNEVEN"])) + _end_column.pack_start(_end_cell, True) + return _end_column + return None + + def createTextBaseColumn(self,args): + """createTextBaseColumn(self,args) + + args: tuple with the args + 0.type: + * float column + * text column + * calculated column + * calculated text + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + + Return a column and its CellREndererText + """ + _column = self.createBaseColumn(args) + _cell = gtk.CellRendererText() + _cell.set_property('foreground-gdk', args[3]) + _column.pack_start(_cell, True) + _column.set_cell_data_func(_cell, self.colorCell, args[4]) + return _column, _cell + + def createBaseColumn(self,args): + """createBaseColumn(self,args) + + args: tuple with the args + 0.type: + * index column + * float column + * text column + * calculated column + * calculated text column + * type column + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + + Return a column + """ + _column = gtk.TreeViewColumn() + _column.set_clickable(True) + _column.connect("clicked", args[1]) + _column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + _column.set_fixed_width(args[2]) + _column.set_resizable(True) + return _column + +class DecompositionList(TreeView): + """gui.DecompositionList: + + Description: + Class to show a budget Decomposition List + Constructor: + DecompositionList(budget, page, path) + budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the view path in the Page + Returns the newly created DecompositionList instance + Ancestry: + +-- object + +-- TreeView + +-- DecompositionList + Atributes: + "budget": Budget to show, base.obra instance. + "widget or __scrolled_window": Window that contains the table, + (gtk.ScrolledWindow) + "path": Pane page identifier + "page": weak reference from Page instance which creates this class + "__active_color": background color of the active cell, a + gtk.gdk.Color object + "__chapter_background_colors": background colors of the Code + column cells when there is a chapter record, + list of gtk.gdk.Color objects [even cell, uneven cell] + "__methond_message": Method to send messages to the page + "__liststore": list model which store the list data + (gtk.ListStore object) + "__treeview": widget for displaying decomposition lists (gtk.TreeView) + "__index_column": Index column (gtk.TreeViewColumn object) + "__code_column": Record code column (gtk.TreeViewColumn) + "__unit_column": Unit of measure column (gtk.TreeViewColumn) + "__description_column": record's short description column + (gtk.TreeViewColumn) + "__measure_column": Measure column (gtk.TreeViewColumn) + "__price_column": Price column (gtk.TreeViewColumn) + "__amount_column": Amount column(gtk.TreeViewColumn) + "__end_column": End empty column (gtk.TreeViewColumn) + "__treeselection": active selection + "__selection_control": state of the selection control (True/False) + "__cursor": cursor position in the table + Methods: + __init__(self, budget) + treeviewCursorChanged(self, treeview) + treeviewClickedEvent(self, widget, event) + treeviewKeyPressEvent(self, widget, event) + moveCursor(self, treeview, step, count) + controlSelection(self, selection) + selectAll(self, column) + setColumnsHeaders(self) + setListstoreValues(self, puntero, treeiter=None) + colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + _claculateAmount(self, row_path, tree_model) + showParentRecord(self, column) + showMessageRecord(self, camino,_code): + showRowRecord(self, treeview, path, column) + runMessage(self, messagem path, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/seg}Page + getBudget(self) + getActivePathRecord(self) + """ + + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path) + + budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + + Sets the init atributes + Creates the init list values in self.__liststore from the budget + showing the top record descomposition + Creates the list in self.__treeview + * Creates the columns and cell + * Sets te the column headers values + * Sets the selection properties + * Connects the events + """ + # TODO: to group all columns in a dicctionary + # Budget + if not isinstance(budget, base.Budget): + raise ValueError, _("Argument must be a Budget object") + self.__budget = budget + self.__page = page + self.__path = path + # ListStore + self.__liststore = gtk.ListStore(object + #, int, int, str, str, str, str, str,str + ) + if path_record is None: + print _("DecompositionList.__init__: Record path can not be None") + path_record = (0,) + self.__active_path_record = path_record + self.setListstoreValues(self.__active_path_record) + # Treeview + self.__treeview = gtk.TreeView(self.__liststore) + self.__treeview.set_enable_search(False) + self.__treeview.set_reorderable(False) + self.__treeview.set_headers_clickable(True) + self.__treeview.show() + # Scrolled_window + self.__scrolled_window = gtk.ScrolledWindow() + self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + self.__scrolled_window.add(self.__treeview) + # colors + _text_color = gtk.gdk.color_parse(globals.color["TEXT"]) + _background_color = [ + gtk.gdk.color_parse(globals.color["UNEVEN"]), + gtk.gdk.color_parse(globals.color["EVEN"])] + self.__chapter_background_colors = [ + gtk.gdk.color_parse(globals.color["CHAPTER-UNEVEN"]), + gtk.gdk.color_parse(globals.color["CHAPTER-EVEN"])] + super(DecompositionList,self).__init__( + [("INDEX",self.selectAll,42), + ("CALCULATEDTEXT", self.showParentRecord, + gtk.Label("A"*10).size_request()[0] +10, + _text_color, _background_color), + ("PIXBUF", self.showParentRecord, 26, _text_color, + _background_color), + ("CALCULATEDTEXT", self.showParentRecord, + gtk.Label(_("a"*4)).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATEDTEXT", self.showParentRecord, + gtk.Label("a"*30).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.showParentRecord, + gtk.Label("a"*10).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.showParentRecord, + gtk.Label("a"*10).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.showParentRecord, + gtk.Label("a"*10).size_request()[0] +10, + gtk.gdk.color_parse(globals.color["CALCULATED-TEXT"]), + _background_color), + ]) + self.__index_column = self.columns[0] + self.__code_column = self.columns[1] + self.__type_column = self.columns[2] + self.__unit_column = self.columns[3] + self.__description_column = self.columns[4] + self.__measure_column = self.columns[5] + self.__price_column = self.columns[6] + self.__amount_column = self.columns[7] + self.__end_column = self.columns[8] + # Index column + self.__treeview.append_column(self.__index_column) + # Code column + self.__treeview.append_column(self.__code_column) + # Type column + self.__treeview.append_column(self.__type_column) + self.chapter_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("CHAPTER-ICON")) + self.unit_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("UNIT-ICON") ) + self.material_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("MATERIAL-ICON") ) + self.machinery_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("MACHINERY-ICON")) + self.labourforce_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("LABOURFORCE-ICON")) + self.__type_column.get_cell_renderers()[0].set_property("pixbuf", + self.labourforce_icon) + + # Unit column + self.__treeview.append_column(self.__unit_column) + # Description column + self.__treeview.append_column(self.__description_column) + # Measure Column + self.__treeview.append_column(self.__measure_column) + # Price column + self.__treeview.append_column(self.__price_column) + # Amount column + self.__treeview.append_column(self.__amount_column) + # End Column + self.__treeview.append_column(self.__end_column) + # Connect + self.__treeview.connect("row-activated", self.showRowRecord) + self.__treeview.connect("move-cursor", self.moveCursor) + self.__treeview.connect("key-press-event", self.treeviewKeyPressEvent) + self.__treeview.connect("button-press-event", self.treeviewClickedEvent) + self.__treeview.connect("cursor-changed", self.treeviewCursorChanged) + # control selection + self.__treeselection = self.__treeview.get_selection() + self.__treeselection.set_mode(gtk.SELECTION_MULTIPLE) + self.__treeselection.set_select_function(self.controlSelection) + self.__selection_control = True + if len(self.__liststore) > 0: + self.__treeview.set_cursor_on_cell((0,),self.__unit_column, + self.__unit_column.get_cell_renderers()[0],True) + self.__treeview.grab_focus() + self.__cursor = self.__treeview.get_cursor() + # Show + self.setColumnsHeaders() + self.__scrolled_window.show() + + def treeviewCursorChanged(self, treeview): + """def treeviewCursorChanged(self, treeview) + + treeview: treewiew widget + Method connected to "cursor-changed" signal + The "cursor-changed" signal is emitted when the cursor moves or is set + Sets the new cursor position in self.__cursor, it is used to avoid + unnecessary changes in cursor position. + """ + event = gtk.get_current_event() + (_cursor_path, _column) = treeview.get_cursor() + if event is None or event.type != gtk.gdk.BUTTON_RELEASE: + if not _column is self.__index_column: + self.__cursor = treeview.get_cursor() + + def treeviewClickedEvent(self, widget, event): + """def treeviewClickedEvent(self, widget, event) + + widget: treewiew widget + event: clicked event + Method connected to "button-press-event" signal + The "button-press-event" signal is emitted when a mouse button is + pressed. + Returns TRUE to stop other handlers from being invoked for the event. + Returns FALSE to propagate the event further. + + The events in end column are ignored. + If the user click in a row of the index column the cursor is moved to + this row but not to the index column + """ + if event.button == 1: + path_at_pos = self.__treeview.get_path_at_pos(int(event.x), + int(event.y)) + if not path_at_pos is None: + _path_cursor, _column, _x, _y = path_at_pos + if _column == self.columns[-1]: + return True + if _column is self.columns[0]: + self.__cursor[0] == _path_cursor + return False + + def treeviewKeyPressEvent(self, widget, event): + """def treeviewKeyPressEvent(self, widget, event) + + widget: treewiew widget + event: Key Press event + Method connected to "key-press-event" signal + The "key-press-event" signal is emitted when the user presses a key + on the keyboard. + Returns :TRUE to stop other handlers from being invoked for the event. + Returns :FALSE to propagate the event further. + + If the user press the right cursor button and the cursor is in the + amount column or pres the left cursor button and the cursor is + in the code column the event is estoped, else the event is propagated. + """ + (_cursor_path, _column) = self.__treeview.get_cursor() + if (event.keyval == gtk.keysyms.Right \ + and _column == self.columns[-2]) \ + or (event.keyval == gtk.keysyms.Left \ + and _column == self.columns[1]): + return True + return False + + def moveCursor(self, treeview, step, count): + """def treeviewKeyPressEvent(self, widget, event) + + treeview: the treeview that received the signal + step: the movement step size + count: the number of steps to take + + Method connected to "move-cursor" signal + The "move-cursor" signal is emitted when the user moves the cursor + using the Right, Left, Up or Down arrow keys or the Page Up, + Page Down, Home and End keys. + + Returns :TRUE if the signal was handled. + """ + return False + + def controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + The selection only run if the user click in the index column, else + the previous selection is erased. + """ + _column = self.__treeview.get_cursor()[1] + if _column is self.columns[0] \ + or self.__selection_control == False: + return True + else: + self.__selection_control = False + self.__treeselection.unselect_all() + self.__selection_control = True + return False + + def selectAll(self, column): + """def selectAll(self, column) + + column: index column + Method connected to "clicked" event in the index column + If the user clickes in the index column header selecs or deselects + all rows + """ + (_model, _pathlist) = self.__treeselection.get_selected_rows() + # it avoid to set cursor in the index column + self.__treeview.set_cursor(self.__cursor[0], self.__cursor[1]) + self.__selection_control = False + if len(_pathlist) == 0: + # select all + self.__treeselection.select_all() + else: + # unselect all + self.__treeselection.unselect_all() + self.__selection_control = True + + def setColumnsHeaders(self): + """def setColumnsHeaders(self) + + Sets the headers column values + """ + _path_record = self.__active_path_record + _number = _path_record[-1] + _budget = self.__budget + _code = _budget.getCode(_path_record) + _decomposition = _budget.getDecomposition(_path_record) + _stryield = _budget.getStrYield(_decomposition.budgetMeasures[0], + _budget.getRecord(_code).recordType) + _record = _budget.getRecord(_code) + _unit = _record.unit + _description = _record.summary + _price = _budget.getStrPriceFromRecord(self.budget.getActiveTitle(), + _record) + # TODO: round to decimal places in amount + _amount = float(_stryield) * float(_price) + if len(_path_record) == 1: # root record + _amount = _price + else: + _parent_code = self.budget.getCode(self.__active_path_record[:-1]) + _parent_record = self.__budget.getRecord(_parent_code) + _amount = _budget.getStrAmount(self.__active_path_record) + + self.__code_column.set_title(_("Code") + chr(10) + "[" + _code + "]") + self.__unit_column.set_title(_("Unit") + chr(10) + "[" + _unit + "]") + self.__description_column.set_title( + _("Description") + chr(10) + "[" + _description + "]") + self.__measure_column.set_title( + _("Measure") + chr(10) + "[" + _stryield + "]") + self.__price_column.set_title( + _("Price") + chr(10) + "[" + _price + "]") + self.__amount_column.set_title( + _("Amount") + chr(10) + "[" + str(_amount) + "]") + + + def setListstoreValues(self, path_record): + """def setListstoreValues(self, path_record) + + path_record: Record path in the budget + Sets the liststore record values from a path record + """ + self.__liststore.clear() + _budget = self.__budget + if not _budget.hasPath(path_record): + raise ValueError, _("Invalid path") + else: + _parent_code = _budget.getCode(path_record) + for N,_code in enumerate(_budget.getchildren(_parent_code)): + _decomposition = _budget.getNDecomposition(_parent_code, N) + _record = _budget.getRecord(_code) + _values = [_record, + #_record.hierarchy, + #_record.type, + #_record.subtype, + #_code, + #_record.unit, + #_record.summary, + #_decomposition.yield_, + #_decomposition.budget[0].yield_, + #_record.prices[_budget.getActiveTitle()].prices] + #_record.getPrice(_budget.getActiveTitle()) + ] + _treeiter = self.__liststore.append(_values) + + def colorCell(self, column, cell_renderer, tree_model, iter, lcolor): + """def colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + + column: the gtk.TreeViewColumn in the treeview + cell_renderer: a gtk.CellRenderer + tree_model: the gtk.TreeModel + iter: gtk.TreeIter pointing at the row + lcolor: list with 2 gtk colors for even and uneven record + + Method connected to "set_cell_data_func" of many column + The set_cell_data_func() method sets the data function (or method) + to use for the column gtk.CellRenderer specified by cell_renderer. + This function (or method) is used instead of the standard attribute + mappings for setting the column values, and should set the attributes + of the cell renderer as appropriate. func may be None to remove the + current data function. The signature of func is: + -def celldatafunction(column, cell, model, iter, user_data) + -def celldatamethod(self, column, cell, model, iter, user_data) + where column is the gtk.TreeViewColumn in the treeview, cell is the + gtk.CellRenderer for column, model is the gtk.TreeModel for the + treeview and iter is the gtk.TreeIter pointing at the row. + + The method sets cell background color and text for all columns. + """ + _row_path = tree_model.get_path(iter) + _number = _row_path[-1] + _record = tree_model[_row_path][0] + if column is self.__index_column: + cell_renderer.set_property('text', str(_number + 1)) + self.__index_column.get_cell_renderers()[1].set_property( + 'cell-background-gdk', lcolor[_number % 2]) + elif column is self.__code_column: + # if the record is a chapter + if tree_model.get_value(iter, 0).recordType.hierarchy == 1: + lcolor = self.__chapter_background_colors + _code = _record.code + cell_renderer.set_property('text', _code) + elif column is self.__unit_column: + _unit = _record.unit + cell_renderer.set_property('text', _unit) + elif column is self.__description_column: + _summary = _record.summary + cell_renderer.set_property('text', _summary) + elif column is self.__measure_column: + _parent_code = self.budget.getCode(self.__active_path_record) + _parent_record = self.__budget.getRecord(_parent_code) + _decomposition = _parent_record.children[_number] + _stryield = self.__budget.getStrYield( + _decomposition.budgetMeasures[0], _parent_record.recordType) + cell_renderer.set_property('text', _stryield) + elif column is self.__price_column: + _price = self.budget.getStrPriceFromRecord( + self.budget.getActiveTitle(), _record) + cell_renderer.set_property('text', _price) + elif column is self.__amount_column: + _parent_code = self.budget.getCode(self.__active_path_record) + _parent_record = self.__budget.getRecord(_parent_code) + _amount = self.budget.getStrAmount( + self.__active_path_record + (_number,)) + cell_renderer.set_property('text', str(_amount)) + elif column is self.__type_column: + _hierarchy = tree_model[_row_path][0].recordType.hierarchy + _type = tree_model[_row_path][0].recordType.type + _subtype = tree_model[_row_path][0].recordType.subtype + if _hierarchy == 1: + cell_renderer.set_property("pixbuf",self.chapter_icon) + else: + if _type == 0: + cell_renderer.set_property("pixbuf",self.unit_icon) + elif _type == 1: + cell_renderer.set_property("pixbuf", + self.labourforce_icon) + elif _type == 2: + cell_renderer.set_property("pixbuf", + self.machinery_icon) + else: + cell_renderer.set_property("pixbuf",self.material_icon) + if self.__treeview.get_cursor() == (_row_path,column): + cell_renderer.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["ACTIVE"])) + else: + cell_renderer.set_property('cell-background-gdk', + lcolor[_number % 2]) + + def showParentRecord(self, column): + """def showParentRecord(self, column) + + column: the column that is clicked + Method connected to "clicked" event of many columns + Show the parent record + """ + _budget = self.__budget + if len(self.__active_path_record) == 1: + # The active record is the root record + # This avoid to move the cursor to the clicked column + self.__treeview.set_cursor(self.__cursor[0], self.__cursor[1]) + else: + _path_record = self.__active_path_record[:-1] + _parent = self.__active_path_record[-1] + self.__active_path_record = _path_record + self.setColumnsHeaders() + self.setListstoreValues(self.__active_path_record) + arg = ( _path_record ) + _page = self.__page() + _page.propagateMessageFrom("change_active", self.__path, arg) + self.__treeview.set_cursor(_parent, self.__cursor[1]) + self.__cursor = self.__treeview.get_cursor() + + def showMessageRecord(self, record_path): + """def showMessageRecord(self, record_path) + + record_path: the path of the record to show + Method connected to "change_active" message + Show the record especified in the "change_active" message + """ + _budget = self.__budget + self.__active_path_record = record_path + self.setColumnsHeaders() + self.setListstoreValues(self.__active_path_record) + self.__treeview.set_cursor((0,)) + + def showRowRecord(self, treeview, treeview_path, column): + """def showRowRecord(self, treeview, treeview_path, column) + + treeview: treview to show + treeview_path: the path of the record to show + code: the code of the record to show + + Method connected to "row-activated" event + The "row-activated" signal is emitted when the row_activated() method + is called or the user double clicks a treeview row. + "row-activated" is also emitted when a non-editable row is selected + and one of the keys: Space, Shift+Space, Return or Enter is pressed. + Show the especified record + """ + if not (column is self.__end_column) and \ + not (column is self.__index_column): + _budget = self.__budget + _model = treeview.get_model() + _iter = _model.get_iter(treeview_path) + _code = _model.get_value(_iter, 0).code + #_code = _model.get_value(_iter, 4) + _path_record = self.__active_path_record + treeview_path + if self.__budget.hasPath(_path_record): + # if this record path is valid + self.__active_path_record = _path_record + self.setColumnsHeaders() + self.setListstoreValues(self.__active_path_record) + self.__treeview.set_cursor((0,)) + _arg = ( _path_record ) + _page = self.__page() + _page.propagateMessageFrom("change_active", self.__path, + _arg ) + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + _path_record = arg + self.showMessageRecord( _path_record) + elif message == "clear": + self._clear() + + def _clear(self): + """def _clear(self) + + it deletes the __budget value + this would not be necessary if there were not circular references, + which are pending to fix + """ + del self.__budget + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__scrolled_window + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + + def getPage(self): + """def getPage(self) + + return the Page + """ + return self.__page + + def setPage(self,page): + """def setPage(self) + + set the Page + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the Budget objet + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + widget = property(getWidget, None, None, + "Pane configuration list") + path = property(getPath, setPath, None, + "path that identifie the item in the page notebook") + page = property(getPage, setPage, None, + "weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active path record") + +class Measure(TreeView): + """gui.Measure: + + Description: + Class to show a Measure List + Constructor: + Measure(budget, page, path) + budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + Returns the newly created DecompositionList instance + Ancestry: + +-- object + +-- TreeView + +-- DecompositionList + Atributes: + "budget": Budget to show, base.obra instance. + "__active_path_record": path of the active record in the budget + "widget or __scrolled_window": Window that contains the table, + (gtk.ScrolledWindow) + "path": Pane page identifier + "page": weak reference from Page instance which creates this class + "__active_color": The background color of the active cell as a + gtk.gdk.Color object + "__chapter_background_colors": The background colors of the Code + column cells when there is a chapter record + as a list of gtk.gdk.Color objects [even cell, uneven cell] + "__methond_message": Method to send messages to the page + "__liststore": list model which store the list data + (gtk.ListStore object) + "__treeview": widget for displaying decomposition lists (gtk.TreeView) + "__index_column": Index column (gtk.TreeViewColumn object) + "__code_column": Record code column (gtk.TreeViewColumn) + "__unit_column": Unit of measure column (gtk.TreeViewColumn) + "__description_column": record's short description column + (gtk.TreeViewColumn) + "__measure_column": Measure column (gtk.TreeViewColumn) + "__price_column": Price column (gtk.TreeViewColumn) + "__amount_column": Amount column(gtk.TreeViewColumn) + "__end_column": End empty column (gtk.TreeViewColumn) + "__treeselection": active selection + "__selection_control": state of the selection control (True/False) + "__cursor": Situation of the cursor in the table + Methods: + __init__(self, budget, page, path, path_record=(0,)) + setListstoreValues(self, path_record) + setColumnsHeaders(self) + controlSelection(self, selection) + showMessageRecord(self, record_path) + treeviewCursorChanged(self, treeview) + moveCursor(self, treeview, step, count) + treeviewClickedEvent(self, widget, event) + treeviewKeyPressEvent(self, widget, event) + runMessage(self, message, path, arg=None) + selectAll(self, column) + colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + _clear(self) + getWidget(self) + {get/set}Path + {get/set}Page + getBudget(self) + getActivePathRecord(self) + """ + + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + path_record: path of the active record in the budget + + Sets the init atributes + Creates the init list values in self.__liststore from the budget + showing the top record from the record with path path_record + Creates the list in self.__treeview + * Creates the columns and cell + * Sets te the column headers values + * Sets the selection properties + * Connects the events + """ + # Seting init args + if not isinstance(budget, base.Budget): + raise ValueError, _("Argument must be a Budget object") + self.__budget = budget + self.__page = page + self.__path = path + if not isinstance(path_record, tuple): + print _("Record path must be a tuple") + path_record = (0,) + self.__active_path_record = path_record + # ListStore + self.__liststore = gtk.ListStore(object) + self.setListstoreValues(self.__active_path_record) + # Treeview + self.__treeview = gtk.TreeView(self.__liststore) + self.__treeview.set_enable_search(False) + self.__treeview.set_reorderable(False) + self.__treeview.set_headers_clickable(True) + self.__treeview.show() + # Scrolled_window + self.__scrolled_window = gtk.ScrolledWindow() + self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + self.__scrolled_window.add(self.__treeview) + # colors + _text_color = gtk.gdk.color_parse(globals.color["TEXT"]) + _calculated_text =gtk.gdk.color_parse(globals.color["CALCULATED-TEXT"]) + _background_color = [ + gtk.gdk.color_parse(globals.color["UNEVEN"]), + gtk.gdk.color_parse(globals.color["EVEN"])] + self.__chapter_background_colors = [ + gtk.gdk.color_parse(globals.color["CHAPTER-UNEVEN"]), + gtk.gdk.color_parse(globals.color["CHAPTER-EVEN"])] + super(Measure,self).__init__( + [("INDEX",self.selectAll,42), + ("PIXBUF", self.passMethod, + gtk.Label("A"*4).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATEDTEXT", self.passMethod, + gtk.Label("A"*12).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*5).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATEDTEXT", self.passMethod, + gtk.Label("A"*12).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _calculated_text, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _calculated_text, _background_color), + ]) + self.__index_column = self.columns[0] + self.__linetype_column = self.columns[1] + self.__comment_column = self.columns[2] + self.__units_column = self.columns[3] + self.__length_column = self.columns[4] + self.__width_column = self.columns[5] + self.__height_column = self.columns[6] + self.__formula_column = self.columns[7] + self.__parcial_column = self.columns[8] + self.__subtotal_column = self.columns[9] + self.__end_column = self.columns[10] + # Index column + self.__treeview.append_column(self.__index_column) + # Linetype column + self.__treeview.append_column(self.__linetype_column) + self.calculatedline_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("CALCULATEDLINE-ICON")) + self.normalline_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("NORMALLINE-ICON") ) + self.parcialline_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("PARCIALLINE-ICON") ) + self.acumulatedline_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("ACUMULATEDLINE-ICON")) + # Comment column + self.__treeview.append_column(self.__comment_column) + # Units column + self.__treeview.append_column(self.__units_column) + # Length column + self.__treeview.append_column(self.__length_column) + # Width_column + self.__treeview.append_column(self.__width_column) + # Height column + self.__treeview.append_column(self.__height_column) + # Formula column + self.__treeview.append_column(self.__formula_column) + # Parcial column + self.__treeview.append_column(self.__parcial_column) + # Subtotal column + self.__treeview.append_column(self.__subtotal_column) + # End Column + self.__treeview.append_column(self.__end_column) + # Connect + self.__treeview.connect("move-cursor", self.moveCursor) + self.__treeview.connect("key-press-event", self.treeviewKeyPressEvent) + self.__treeview.connect("button-press-event", self.treeviewClickedEvent) + self.__treeview.connect("cursor-changed", self.treeviewCursorChanged) + # control selection + self.__treeselection = self.__treeview.get_selection() + self.__treeselection.set_mode(gtk.SELECTION_MULTIPLE) + self.__treeselection.set_select_function(self.controlSelection) + self.__selection_control = True + self.__treeview.set_cursor_on_cell((1,), self.columns[1], + self.columns[1].get_cell_renderers()[0],True) + self.__treeview.grab_focus() + self.__cursor = self.__treeview.get_cursor() + # Show + self.setColumnsHeaders() + self.__scrolled_window.show() + + def passMethod(self, args): + pass + + def setListstoreValues(self, path_record): + """def setListstoreValues(self, path_record) + + path_record: Record path in the budget + Sets the liststore record values from a path record + """ + self.__liststore.clear() + _budget = self.__budget + if not _budget.hasPath(path_record): + raise ValueError, _("Invalid path") + else: + _measure = _budget.getMeasure(path_record) + if isinstance(_measure, base.Measure): + _lines = _measure.lines + for _line in _lines: + _values = [ + _line, +## _line.type, +## _line.comment, +## _line.units, +## _line.length, +## _line.width, +## _line.height + ] + _treeiter = self.__liststore.append(_values) + else: + raise ValueError, utils.mapping(_("measure must be a Measure "\ + "object. Type: $1"), (type(_measure),)) + def setColumnsHeaders(self): + """def setColumnsHeaders(self) + + Sets the headers column values + """ + _measure = self.__budget.getMeasure(self.__active_path_record) + _DS = self.__budget.getDecimals("DS") + _total = _measure.measure + _total_str = ("%." + str(_DS) + "f" ) % _total + self.columns[1].set_title(_("Type")) # Σ parcial Σ total + self.columns[2].set_title(_("Comment")) + self.columns[3].set_title(_("N\n(a)")) + self.columns[4].set_title(_("Length\n(b)")) + self.columns[5].set_title(_("Width\n(c)")) + self.columns[6].set_title(_("Height\n(d)")) + self.columns[7].set_title(_("Formula")) + self.columns[8].set_title(_("Parcial\n[%s]" % _total_str)) + self.columns[9].set_title(_("Subtotal")) + def controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + The selection only run if the user click in the index column, else + the previous selection is erased. + """ + _column = self.__treeview.get_cursor()[1] + if _column is self.columns[0] \ + or self.__selection_control == False: + return True + else: + self.__selection_control = False + self.__treeselection.unselect_all() + self.__selection_control = True + return False + + def showMessageRecord(self, record_path): + """def showMessageRecord(self, record_path) + + record_path: the path of the record to show + Method connected to "change_active" message + Show the record especified in the "change_active" message + """ + _budget = self.__budget + self.__active_path_record = record_path + self.setColumnsHeaders() + self.setListstoreValues(self.__active_path_record) + self.__treeview.set_cursor((0,)) + + def treeviewCursorChanged(self, treeview): + """def treeviewCursorChanged(self, treeview) + + treeview: treewiew widget + Method connected to "cursor-changed" signal + The "cursor-changed" signal is emitted when the cursor moves or is set + Sets the new cursor position in self.__cursor, it is used to avoid + unnecessary changes in cursor position. + """ + event = gtk.get_current_event() + (_cursor_path, _column) = treeview.get_cursor() + if event is None or event.type != gtk.gdk.BUTTON_RELEASE: + if not _column is self.__index_column: + self.__cursor = treeview.get_cursor() + + def moveCursor(self, treeview, step, count): + """def treeviewKeyPressEvent(self, widget, event) + + treeview: the treeview that received the signal + step: the movement step size + count: the number of steps to take + + Method connected to "move-cursor" signal + The "move-cursor" signal is emitted when the user moves the cursor + using the Right, Left, Up or Down arrow keys or the Page Up, + Page Down, Home and End keys. + + Returns :TRUE if the signal was handled. + """ + return False + + def treeviewClickedEvent(self, widget, event): + """def treeviewClickedEvent(self, widget, event) + + widget: treewiew widget + event: clicked event + Method connected to "button-press-event" signal + The "button-press-event" signal is emitted when a mouse button is + pressed. + Returns TRUE to stop other handlers from being invoked for the event. + Returns FALSE to propagate the event further. + + The events in end column are ignored. + If the user click in a row of the index column the cursor is moved to + this row but not to the index column + """ + if event.button == 1: + path_at_pos = self.__treeview.get_path_at_pos(int(event.x), + int(event.y)) + if not path_at_pos is None: + _path_cursor, _column, _x, _y = path_at_pos + if _column == self.columns[-1]: + return True + if _column is self.columns[0]: + self.__cursor[0] == _path_cursor + return False + + def treeviewKeyPressEvent(self, widget, event): + """def treeviewKeyPressEvent(self, widget, event) + + widget: treewiew widget + event: Key Press event + Method connected to "key-press-event" signal + The "key-press-event" signal is emitted when the user presses a key + on the keyboard. + Returns :TRUE to stop other handlers from being invoked for the event. + Returns :FALSE to propagate the event further. + + If the user press the right cursor button and the cursor is in the + amount column or pres the left cursor button and the cursor is + in the code column the event is estoped, else the event is propagated. + """ + (_cursor_path, _column) = self.__treeview.get_cursor() + if (event.keyval == gtk.keysyms.Right \ + and _column == self.columns[-2]) \ + or (event.keyval == gtk.keysyms.Left \ + and _column == self.columns[1]): + return True + return False + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + _path_record = arg + self.showMessageRecord( _path_record) + elif message == "clear": + self._clear() + + def selectAll(self, column): + """def selectAll(self, column) + + column: index column + Method connected to "clicked" event in the index column + If the user clickes in the index column header selecs or deselects + all rows + """ + (_model, _pathlist) = self.__treeselection.get_selected_rows() + # it avoid to set cursor in the index column + self.__treeview.set_cursor(self.__cursor[0], self.__cursor[1]) + self.__selection_control = False + if len(_pathlist) == 0: + # select all + self.__treeselection.select_all() + else: + # unselect all + self.__treeselection.unselect_all() + self.__selection_control = True + + def colorCell(self, column, cell_renderer, tree_model, iter, lcolor): + """def colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + + column: the gtk.TreeViewColumn in the treeview + cell_renderer: a gtk.CellRenderer + tree_model: the gtk.TreeModel + iter: gtk.TreeIter pointing at the row + lcolor: list with 2 gtk colors for even and uneven record + + Method connected to "set_cell_data_func" of many column + The set_cell_data_func() method sets the data function (or method) + to use for the column gtk.CellRenderer specified by cell_renderer. + This function (or method) is used instead of the standard attribute + mappings for setting the column values, and should set the attributes + of the cell renderer as appropriate. func may be None to remove the + current data function. The signature of func is: + -def celldatafunction(column, cell, model, iter, user_data) + -def celldatamethod(self, column, cell, model, iter, user_data) + where column is the gtk.TreeViewColumn in the treeview, cell is the + gtk.CellRenderer for column, model is the gtk.TreeModel for the + treeview and iter is the gtk.TreeIter pointing at the row. + + The method sets cell background color for all columns + and text for index and amount columns. + """ + _row_path = tree_model.get_path(iter) + _number = _row_path[-1] + if column is self.__index_column: + cell_renderer.set_property('text', str(_number + 1)) + self.__index_column.get_cell_renderers()[1].set_property( + 'cell-background-gdk', lcolor[_number % 2]) + elif column is self.__linetype_column: + _measure = tree_model[_row_path][0] + _type = _measure.lineType + if _type == 0: + cell_renderer.set_property("pixbuf",self.normalline_icon) + elif _type == 1: + cell_renderer.set_property("pixbuf",self.parcialline_icon) + elif _type == 2: + cell_renderer.set_property("pixbuf", + self.acumulatedline_icon) + else: #elif _type == 3: + cell_renderer.set_property("pixbuf", + self.calculatedline_icon) + + elif column is self.__comment_column: + _measure = tree_model[_row_path][0] + _comment = str(_measure.comment) + cell_renderer.set_property('text', _comment) + elif column is self.__units_column: + _measure = tree_model[_row_path][0] + _units = _measure.units + if isinstance(_units, float): + _DN = self.__budget.getDecimals("DN") + _units = ("%." + str(_DN) + "f" ) % _units + cell_renderer.set_property('text', _units) + elif column is self.__length_column: + _measure = tree_model[_row_path][0] + _length = _measure.length + if isinstance(_length, float): + _DD = self.__budget.getDecimals("DD") + _length = ("%." + str(_DD) + "f" ) % _length + cell_renderer.set_property('text', _length) + elif column is self.__width_column: + _measure = tree_model[_row_path][0] + _width = _measure.width + if isinstance(_width, float): + _DD = self.__budget.getDecimals("DD") + _width = ("%." + str(_DD) + "f" ) % _width + cell_renderer.set_property('text', _width) + elif column is self.__height_column: + _measure = tree_model[_row_path][0] + _height = _measure.height + if isinstance(_height, float): + _DD = self.__budget.getDecimals("DD") + _height = ("%." + str(_DD) + "f" ) % _height + cell_renderer.set_property('text', _height) + elif column is self.__formula_column: + _measure = tree_model[_row_path][0] + _formula = _measure.formula + cell_renderer.set_property('text', _formula) + elif column is self.__parcial_column: + _measure_line = tree_model[_row_path][0] + _parcial = _measure_line.parcial + _type = _measure_line.lineType + if _type == 1 or _type == 2: + _parcial = "" + else: + if isinstance(_parcial, float): + _DS = self.__budget.getDecimals("DS") + _parcial = ("%." + str(_DS) + "f" ) % _parcial + cell_renderer.set_property('text', _parcial) + elif column is self.__subtotal_column: + _measure_line = tree_model[_row_path][0] + _type = _measure_line.lineType + if _type == 1 or _type == 2: + if _type == 1: + _color = gtk.gdk.color_parse( + globals.color["SUBTOTAL-PARCIAL"]) + _subtotal = _measure_line.parcial_subtotal + else: #elif _type == 2: + _color = gtk.gdk.color_parse(globals.color["SUBTOTAL"]) + _subtotal = _measure_line.acumulated_subtotal + lcolor = [_color, _color] + if isinstance(_subtotal, float): + _DS = self.__budget.getDecimals("DS") + _subtotal= ("%." + str(_DS) + "f" ) % _subtotal + cell_renderer.set_property('text', _subtotal) + else: + cell_renderer.set_property('text', "") + + if self.__treeview.get_cursor() == (_row_path,column): + cell_renderer.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["ACTIVE"])) + else: + cell_renderer.set_property('cell-background-gdk', + lcolor[_number % 2]) + + def _clear(self): + """def _clear(self) + + it deletes the __budget value + this would not be necessary if there were not circular references, + which are pending to fix + """ + del self.__budget + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__scrolled_window + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + def getPage(self): + """def getPage(self) + + return the Page + """ + return self.__page + + def setPage(self,page): + """def setPage(self) + + set the Page + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the Budget objet + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + widget = property(getWidget, None, None, + "Pane configuration list") + path = property(getPath, setPath, None, + "Path that identifies the item in the page notebook") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active Code") + +class Description(object): + """gui.Description + + Description: + Class to show a description text of a record in a pane + Constructor: + Description(budget, code) + budget: budget + code: code record + Ancestry: + +-- object + +-- Description + Atributes: + "widget": the main widget (gtk.ScrolledWindow object) + "path": the tuple that identifies the pane in the notebook page + TODO + "budget": The budget (base.obra objetc) + "active_code": The active code of the record + "__textbuffer": The textbuffer of the textview that contain + the record text. + "__label": The gtk.label with the title of the pane + Methods: + __init__(self, budget, code) + setActiveCode(self, code) + runMessage(self, message, nt, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/seg}Page + getBudget(self) + getActviCode(self) + """ + # TODO: make standar: "DecompositonList and Description" + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: the budget (base.obra object) + page: weak reference from Page instance which creates this class + path: the path position of the description in the page + path_record: the path of the active record + + Creates an shows the scroledwindow that contain the description text + of the record to be showed in a pane. + """ + self.__budget = budget + self.__page = page + self.__path = path + self.__active_path_record = path_record + _budget = budget + _text = _budget.getRecord(self.__budget.getCode( + self.__active_path_record)).text + _scrollwindow = gtk.ScrolledWindow() + _scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _textview = gtk.TextView() + _textview.set_wrap_mode(gtk.WRAP_WORD) + self.__textbuffer = _textview.get_buffer() + self.__textbuffer.set_text(_text) + _textview.show() + _hbox = gtk.HBox() + _hbox.pack_start(_textview, True, True, 5) + _hbox.show() + _vbox = gtk.VBox() + self.__label = gtk.Label(utils.mapping(_("Description text of the "\ + "record $1"), (self.__budget.getCode( + self.__active_path_record),))) + self.__label.set_alignment(0, 0) + self.__label.show() + _vbox.pack_start(self.__label, False, False, 5) + _vbox.pack_start(_hbox, True, True, 5) + _vbox.show() + _scrollwindow.add_with_viewport(_vbox) + _scrollwindow.show() + self.__widget = _scrollwindow + + + def setActivePathRecord(self, path_record): + """def setActivePathRecord(self, path_record)) + + path_record: active path record + Set the new path code to show its description text. + """ + _budget = self.__budget + self.__active_path_record = path_record + _code = _budget.getCode(self.__active_path_record) + self.__label.set_text(utils.mapping(_("Description text of the record "\ + "$1"), (_code,))) + _text = _budget.getRecord(_code).text + self.__textbuffer.set_text(_text) + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + self.setActivePathRecord(arg) + elif message == "clear": + self._clear() + + def _clear(self): + """def _clear(self) + + Delete all instance atributes + """ + del self.__widget + del self.__path + del self.__budget + del self.__active_code + del self.__textbuffer + del self.__label + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + + def getPage(self): + """def getPage(self) + + return the weak reference from Page instance + """ + return self.__page + + def setPage(self, page): + """def setPage(self) + + set the weak reference from Page instance + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the budget object + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + path = property(getPath, setPath, None, + "Path that identifie the item in the page notebook") + widget = property(getWidget, None, None, + "The main widget (gtk.ScrolledWindow)") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active Path Record") + +class Sheet(object): + """gui.Sheet + + Description: + Class to show a sheeet of conditions text of a record in a pane + Constructor: + Sheet(budget, code) + budget: budget object + code: code record + Ancestry: + +-- object + +-- Sheet + Atributes: + "widget": the main widget (gtk.ScrolledWindow object) + "path": the tuple that identifies the pane in the notebook page + "budget": The budget (base.obra objetc) + "active_path_record": The active path record + "page": weak reference from Page instance which creates this class + + "__textbuffer": The textbuffer of the textview that contain + the record text. + "__label": The gtk.label with the title of the pane + "__field_liststore": the field liststore + "__field_treeview": the field treeview + "__field_selection": the field selected in field treview + "__section_liststore": the section liststore + "__section_treeview": the section treeview + "__section_selection": the section selected in the section treeview + Methods: + __init__(self, budget, code) + setFields(self) + setSection(self) + setText(self) + field_controlSelection(self, selection) + section_controlSelection(self, selection) + runMessage(self, message, nt, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/set}Page + getBudget(self) + getActviPathRecord(self) + """ + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: the budget (base.obra object) + page: weak reference from Page instance which creates this class + path: the path position of the description in the page + path_record: the path of the active record + Creates an shows the scroledwindow that contain the description text + of the record to be showed in a pane. + """ + self.__budget = budget + self.__page = page + self.__path = path + self.__active_path_record = path_record + _budget = budget + + _main_box = gtk.VBox() + + self.__label = gtk.Label(utils.mapping(_("Sheet of Conditions of the "\ + "record $1"), (self.__budget.getCode( + self.__active_path_record),))) + self.__label.set_alignment(0, 0) + self.__label.show() + + _frame = gtk.Frame() + _frame.set_shadow_type(gtk.SHADOW_IN) + _frame_box = gtk.VBox() + _list_box = gtk.HBox() + + self.__field_liststore = gtk.ListStore(str, str) + self.__field_treeview = gtk.TreeView(self.__field_liststore) + _field_treeselection = self.__field_treeview.get_selection() + _field_treeselection.set_mode(gtk.SELECTION_SINGLE) + self.__field_selection = None + _field_treeselection.set_select_function( + self.field_controlSelection) + self.__field_treeview.show() + _fieldcode_cell = gtk.CellRendererText() + _field_column = gtk.TreeViewColumn(_("Field")) + _field_column.pack_start(_fieldcode_cell, False) + _field_cell = gtk.CellRendererText() + _field_column.pack_end(_field_cell, True) + _field_column.add_attribute(_fieldcode_cell, "text", 0) + _field_column.add_attribute(_field_cell, "text", 1) + self.__field_treeview.append_column(_field_column) + _field_scrollwindow = gtk.ScrolledWindow() + _field_scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _field_scrollwindow.add(self.__field_treeview) + _field_scrollwindow.show() + + self.__section_liststore = gtk.ListStore(str, str) + self.__section_treeview = gtk.TreeView(self.__section_liststore) + _section_treeselection = self.__section_treeview.get_selection() + _section_treeselection.set_mode(gtk.SELECTION_SINGLE) + self.__section_selection = None + _section_treeselection.set_select_function( + self.section_controlSelection) + self.__section_treeview.show() + _sectioncode_cell = gtk.CellRendererText() + _section_column = gtk.TreeViewColumn(_("Section")) + _section_column.pack_start(_sectioncode_cell, False) + _section_column.add_attribute(_sectioncode_cell, "text", 0) + _section_cell = gtk.CellRendererText() + _section_column.pack_end(_section_cell, True) + _section_column.add_attribute(_section_cell, "text", 1) + self.__section_treeview.append_column(_section_column) + _section_scrollwindow = gtk.ScrolledWindow() + _section_scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _section_scrollwindow.add(self.__section_treeview) + _section_scrollwindow.show() + + _list_box.pack_start(_field_scrollwindow, True, True, 5) + _list_box.pack_start(_section_scrollwindow, True, True, 5) + _list_box.show() + + _scrollwindow = gtk.ScrolledWindow() + _scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _textview = gtk.TextView() + _textview.set_wrap_mode(gtk.WRAP_WORD) + self.__textbuffer = _textview.get_buffer() + _textview.show() + _hbox = gtk.HBox() + _hbox.pack_start(_textview, True, True, 5) + _hbox.show() + + _frame_box.pack_start(self.__label, False, False, 5) + _frame_box.pack_start(_list_box, False, False, 5) + _frame_box.show() + _frame.add(_frame_box) + _frame.show() + _main_box.pack_start(_frame, False) + + + _vbox = gtk.VBox() + _vbox.pack_start(_hbox, True, True, 5) + _vbox.show() + _main_box.pack_start(_scrollwindow, True, True, 5) + _main_box.show() + + _scrollwindow.add_with_viewport(_vbox) + _scrollwindow.show() + self.__widget = _main_box + + self.setFields() + + def setFields(self): + """setFields(self) + + Set the fields items in the field treeview + """ + _record = self.__budget.getRecord(self.__budget.getCode( + self.__active_path_record)) + _sheet = _record.getSheet() + _field_list = _sheet.getFields() + self.__field_liststore.clear() + for _field in _field_list: + _field_text = self.__budget.getSheetField(_field) + _iter = self.__field_liststore.append([_field, _field_text]) + _treeselection = self.__field_treeview.get_selection() + _treeselection.select_path(0) + + def setSection(self): + """setSection(self) + + Set the section items in the section treeview + """ + self.__section_liststore.clear() + if not self.__field_selection is None: + _record = self.__budget.getRecord(self.__budget.getCode( + self.__active_path_record)) + _sheet = _record.getSheet() + _section_list = _sheet.getSections(self.__field_selection) + for _section in _section_list: + _section_text = self.__budget.getSheetSection(_section) + _iter = self.__section_liststore.append([_section, _section_text]) + _treeselection = self.__section_treeview.get_selection() + _treeselection.select_path(0) + + def setText(self): + """setText(self) + + Set the text in the textview + """ + if not self.__section_selection is None and\ + not self.__field_selection is None: + _record = self.__budget.getRecord(self.__budget.getCode( + self.__active_path_record)) + _sheet = _record.getSheet() + _paragraph_code = _sheet.getParagraph(self.__field_selection, + self.__section_selection) + _paragraph = self.__budget.getSheetParagraph(_paragraph_code) + self.__textbuffer.set_text(_paragraph) + else: + self.__textbuffer.set_text("") + + def field_controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() in field treeview + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + When a user select a row in the field treeview the section treeview is + reloaded to show the sections of this field and already the text sheet. + """ + _treeiter = self.__field_liststore.get_iter(selection) + self.__field_selection = self.__field_liststore.get_value(_treeiter, 0) + self.setSection() + return True + + def section_controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() in sector treeview + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + When a user select a row in the field treeview the text sheet for this + section in showed + """ + _treeiter = self.__section_liststore.get_iter(selection) + self.__section_selection = self.__section_liststore.get_value(_treeiter, 0) + self.setText() + return True + + def setActivePathRecord(self, path_record): + """def setActivePathRecord(self, path_record)) + + path_record: active path record + + Set the new path code to show its sheet of condition text. + """ + self.__field_selection = None + self.__field_liststore.clear() + self.__section_selection = None + self.__section_liststore.clear() + self.__textbuffer.set_text("") + _budget = self.__budget + self.__active_path_record = path_record + _code = _budget.getCode(self.__active_path_record) + self.__label.set_text(utils.mapping(_("Sheet2 of Conditions of the "\ + "record $1"), (_code,))) + self.setFields() + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + self.setActivePathRecord(arg) + elif message == "clear": + self._clear() + + def _clear(self): + """def _clear(self) + + Deletes all the instance atributes + """ + del self.__page + del self.__widget + del self.__path + del self.__budget + del self.__active_code + del self.__textbuffer + del self.__label + del self.__textbuffer + del self.__label + del self.__field_liststore + del self.__field_treeview + del self.__field_selection + del self.__section_liststore + del self.__section_treeview + del self.__section_selection + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__page + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + + def getPage(self): + """def getPage(self) + + return the weak reference from Page instance + """ + return self.__page + + def setPage(self, page): + """def setPage(self) + + set the weak reference from Page instance + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the budget object + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + path = property(getPath, setPath, None, + "Path that identifie the item in the page notebook") + widget = property(getWidget, None, None, + "Lista de configuracion de vistas") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active Path Record") + +class FileView(object): + """gui.FileView + + Description: + Class to show the file icons of a record in a pane + Constructor: + Description(budget, code) + budget: budget + code: code record + Ancestry: + +-- object + +-- Description + Atributes: + "widget": the main widget (gtk.ScrolledWindow object) + "__icon_box": the box that contains the icon + "path": the tuple that identifies the pane in the notebook page + "budget": The budget (base.obra objetc) + "active_code": The active code of the record + Methods: + __init__(self, budget, code) + setActiveCode(self, code) + runMessage(self, message, nt, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/seg}Page + getBudget(self) + getActviCode(self) + """ + + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: the budget (base.obra object) + page: weak reference from Page instance which creates this class + path: the path position of the description in the page + path_record: the path of the active record + + Creates an shows the scroledwindow that contain icon files + of the record to be showed in a pane. + """ + self.__budget = budget + self.__page = page + self.__path = path + self.__active_path_record = path_record + self.__active_code = budget.getCode(self.__active_path_record) + _budget = budget + _record = self.__budget.getRecord(self.__budget.getCode( + self.__active_path_record)) + + self.__icon_box = self.getIconBox(_record) + _scrollwindow = gtk.ScrolledWindow() + _scrollwindow.set_policy(gtk.POLICY_ALWAYS, + gtk.POLICY_NEVER) + + self.__icon_box.show() + _scrollwindow.add_with_viewport(self.__icon_box) + _scrollwindow.show() + self.__widget = _scrollwindow + + def getIconBox(self, record): + """getIconBox(self, record) + + record: the active record object + + Creates and returns the box whith te icon files of the active record. + """ + ## TODO: add others filetypes: avi, pdf, ppt... + _files = record.getFiles() + _hbox = gtk.HBox() + _frame = gtk.Frame() + _frame.set_shadow_type(gtk.SHADOW_IN) + for _file in _files: + _path = os.path.dirname(self.__budget.filename) + _file_path = os.path.join(_path, _file.name) + _filetype = utils.getFiletype(_file_path) + _box = gtk.VBox() + if _filetype == "image": + _event_box = gtk.EventBox() + try: + _image_pixbuf = gtk.gdk.pixbuf_new_from_file(_file_path) + _image_pixbuf = _image_pixbuf.scale_simple(64, 64, + gtk.gdk.INTERP_BILINEAR) + except: + _image_pixbuf = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("IMAGE-ICON")) + _image_pixbuf = _image_pixbuf.scale_simple(64, 64, + gtk.gdk.INTERP_BILINEAR) + _image_icon = gtk.Image() + _image_icon.set_from_pixbuf(_image_pixbuf) + _image_icon.show() + _event_box.add(_image_icon) + _box.pack_start(_event_box, False, False, 5) + _event_box.connect("button-press-event", self.launchFile, + "image", _file_path) + _event_box.show() + + elif _filetype == "dxf": + _event_box = gtk.EventBox() + _dxf_icon = gtk.Image() + _dxf_pixbuf = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("DXF-ICON")) + _dxf_pixbuf = _dxf_pixbuf.scale_simple(64, 64, + gtk.gdk.INTERP_BILINEAR) + _dxf_icon.set_from_pixbuf(_dxf_pixbuf) + _dxf_icon.show() + _event_box.add(_dxf_icon) + _box.pack_start(_event_box, False, False, 5) + _event_box.connect("button-press-event", self.launchFile, + "dxf", _file_path) + _event_box.show() + _label_event_box = gtk.EventBox() + _label = gtk.Label(_file.name) + _label_event_box.add(_label) + _label_event_box.show() + _label.show() + _box.pack_start(_label_event_box, False, False, 5) + _box.show() + _hbox.pack_start(_box, False, False, 5) + _hbox.show() + _frame.add(_hbox) + return _frame + + def launchFile(self, widget, event, kind, file_path): + """launchFile(self, widget, event, kind, file_path) + + widget: the widget that emit the signal + event: the event that emit the signal + king: kind of file + file_path: the path file to be launch + + Launch the file if a double click emit the signal. + Method connected to "button-press-event" signal in images event box + """ + if event.type is gtk.gdk._2BUTTON_PRESS: + openwith.launch_file(kind, file_path) + + def setActivePathRecord(self, path_record): + """def setActivePathRecord(self, path_record)) + + path_record: active path record + Set the new path code to show its description text. + """ + _budget = self.__budget + self.__active_path_record = path_record + _code = _budget.getCode(self.__active_path_record) + _record = self.__budget.getRecord(_code) + self.__icon_box.destroy() + self.__icon_box = self.getIconBox(_record) + self.__icon_box.show() + self.__widget.add_with_viewport(self.__icon_box) + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + self.setActivePathRecord(arg) + elif message == "clear": + self._clear() + + def _clear(self): + """def _clear(self) + + Delete all instance atributes + """ + del self.__hbox + del self.__widget + del self.__path + del self.__budget + del self.__active_code + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + + def getPage(self): + """def getPage(self) + + return the weak reference from Page instance + """ + return self.__page + + def setPage(self, page): + """def setPage(self) + + set the weak reference from Page instance + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the budget object + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + path = property(getPath, setPath, None, + "Path that identifie the item in the page notebook") + widget = property(getWidget, None, None, + "The main widget (gtk.ScrolledWindow)") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active Path Record") + + +class TextWindow(object): + """gui.TextWindow + + Description: + Class to show a description text of a record in a new gtk window + Constructor: + TextWindow(code, text) + code: code of the record + text: description text of the record + Ancestry: + +-- object + +-- TextWindow + Atributes: + Methods: + __init__(self, code, text) + main(self) + destroy(self, widget) + """ + + def __init__(self, code, text): + """def __init__(self, code, text) + + code: code of the record + text: description text of the record + Creates an shows the window. + """ + _window = gtk.Window(gtk.WINDOW_TOPLEVEL) + _window.set_resizable(True) + _window.set_default_size(700, 300) + _window.set_title(utils.mapping(_("$1 text"), (code,))) + _window.set_border_width(0) + _box1 = gtk.VBox(False, 0) + _window.add(_box1) + _box1.show() + _sw = gtk.ScrolledWindow() + _sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + _textview = gtk.TextView() + _textview.set_wrap_mode(gtk.WRAP_WORD) + _textbuffer = _textview.get_buffer() + _sw.add(_textview) + _sw.show() + _textview.show() + _box1.pack_start(_sw) + _textbuffer.set_text(text) + _window.connect("destroy", self.destroy) + _window.show() + + def main(self): + """def main(self) + + Starts the GTK+ event processing loop. + """ + gtk.main() + + def destroy(self, widget): + """destroy(self, widget) + widget: the widget where the event is emitted from + Method connected to "destroy" signal of window widget + This signal is emited when the method connected to "delete_event" + signal returns True or when the program call the destroy() method of + the gtk.Window widget. + The window is closed and the GTK+ event processing loop is ended. + """ + gtk.main_quit() + +class CompanyView(object): + """gui.CompanyView: + + Description: + Class to show the company records of a budget + Constructor: + CompanyView(budget, page, path) + budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + Returns the newly created CompanyView instance + Ancestry: + +-- object + +-- CompanyView + Atributes: + "budget": Budget to show, base.obra instance. + "active_path_record" + "widget": Window that contains the main widget, + (gtk.ScrolledWindow) + "path": Pane page identifier + "page": weak reference from Page instance which creates this class + "__methond_message": Method to send messages to the page + Methods: + __init__(self, budget, page, path, path_record=(0,)) + runMessage(self, message, path, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/set}Page + getBudget(self) + getActivePathRecord(self) + """ + + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + path_record: path of the active record in the budget + + Sets the init atributes + Creates the + + """ + self.__selection = None + # Seting init args + if not isinstance(budget, base.Budget): + raise ValueError, _("Argument must be a Budget object") + self.__budget = budget + self.__page = page + self.__path = path + self.__active_path_record = path_record + # HVox + self.__hbox = gtk.HPaned() + self.__hbox.set_position(230) + # TreeStore + self.__treestore = gtk.TreeStore(str, str) + self.setTreeStoreValues() + # Select Treeview + self.__select_treeview = gtk.TreeView(self.__treestore) + self.__select_treeview.set_enable_search(False) + self.__select_treeview.set_reorderable(False) + self.__select_treeview.set_headers_visible(False) + self.__select_treeview.show() + # Scrolled_window + self.__scrolled_window = gtk.ScrolledWindow() + self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + self.__scrolled_window.add(self.__select_treeview) + # colors + _text_color = gtk.gdk.color_parse(globals.color["TEXT"]) + _background_color = [ + gtk.gdk.color_parse(globals.color["UNEVEN"]), + gtk.gdk.color_parse(globals.color["EVEN"])] + self.__code_column = gtk.TreeViewColumn() + self.__code_column.set_clickable(True) + self.__code_column.set_fixed_width(200) + _code_cell = gtk.CellRendererText() + _code_cell.set_property('foreground-gdk', _text_color) + self.__code_column.pack_start(_code_cell, True) + self.__code_column.add_attribute(_code_cell, 'text', 0) + _summary_cell = gtk.CellRendererText() + _summary_cell.set_property('foreground-gdk', _text_color) + self.__code_column.pack_start(_summary_cell, True) + self.__code_column.add_attribute(_summary_cell, 'text', 1) + # Index column + self.__select_treeview.append_column(self.__code_column) + # control selection + self.__treeselection = self.__select_treeview.get_selection() + self.__treeselection.set_mode(gtk.SELECTION_SINGLE) + self.__treeselection.set_select_function(self.controlSelection) + # Show + self.setColumnsHeaders() + self.__scrolled_window.show() + # Option View + self.__option_View = OptionView("") + # Selection + self.__select_treeview.set_cursor((0,), None, False) + self.__select_treeview.grab_focus() + # + self.__hbox.add1(self.__scrolled_window) + self.__hbox.add2(self.__option_View.widget) + self.__hbox.show() + self.__widget = self.__hbox + + def setOptions(self, type): + if type == "company": + _options = [("code", _("Code"), "string", + _("""Code that define the company""")), + ("summary", _("Summary"), "string", + _("""Summary of the company name""")), + ("name", _("Name"), "string", + _("""Complete name""")), + ("cif", _("CIF"), "string", + _("""Fiscal identifier number""")), + ("web", _("Web"), "string", + _("""Company web page""")), + ("email", _("Email"), "string", + _("""Company email""")), + ] + self.__option_View.setOptions(_options) + elif type == "office": + _options = [("type", _("Type"), "string", + _("""Type of Office: + C: Central office + D: Local office + R: Performer""")), + ("subname", _("Name"), "string", + _("Office name")), + ("address", _("Address"), "string",""), + ("postal code", _("Postal code"), "string",""), + ("town", _("Town"), "string",""), + ("province", _("Province"), "string",""), + ("country", _("Country"), "string",""), + ("phone", _("Phone"), "list", + _("Phone numbers of the office")), + ("fax", _("Fax"), "list", + _("Fax numbers of the office")), + ("contact person", _("Contact person"), "string", + _("Contact persons in the office")), + ] + self.__option_View.setOptions(_options) + else: + print _("Unknow Option Type") + def setOptionValues(self, company_key): + self.__option_View.setValues(_values) + + def setTreeStoreValues(self): + """def setListstoreValues(self) + + Sets the treestore values from the budget + """ + _budget = self.__budget + _company_keys = _budget.getCompanyKeys() + for _company_key in _company_keys: + _company = _budget.getCompany(_company_key) + _values = [_company_key, _company.summary] + _treeiter = self.__treestore.append(None, _values) + _offices = _company.offices + for _office in _offices: + # TODO: Test offices + _values = [_office.officeType, _office.subname] + self.__treestore.append(_treeiter, _values) + + def setColumnsHeaders(self): + """def setColumnsHeaders(self) + + Sets the headers column values + """ + #self.columns[1].set_title(_("Type")) # Σ parcial Σ total + #self.columns[2].set_title(_("Comment")) + #self.columns[3].set_title(_("N")) + #self.columns[4].set_title(_("Length")) + #self.columns[5].set_title(_("Width")) + #self.columns[6].set_title(_("Height")) + pass + + def controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: selection + + Method connected to set_selection_function() + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + The selection changes the company/office in the option treeview + """ + if len(selection) == 1: + # The selection is a company + _company_key = self.__treestore[selection][0] + _company = self.__budget.getCompany(_company_key) + _selection = "company" + _values = _company.values + else: + # The selection is a office + _company_key = self.__treestore[selection[:1]][0] + _company = self.__budget.getCompany(_company_key) + _selection = "office" + _office = _company.offices[selection[1]] + _values = _office.values + if not self.__selection == _selection: + self.__selection = _selection + self.setOptions(_selection) + self.__option_View.setValues(_values) + + return True + + def showMessageRecord(self, record_path): + """def showMessageRecord(self, record_path) + + record_path: the path of the record to show + Method connected to "change_active" message + Show the record especified in the "change_active" message + """ + self.__active_path_record = record_path + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + _path_record = arg + self.showMessageRecord( _path_record) + pass + elif message == "clear": + self._clear() + + def colorCell(self, column, cell_renderer, tree_model, iter, lcolor): + """def colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + + column: the gtk.TreeViewColumn in the treeview + cell_renderer: a gtk.CellRenderer + tree_model: the gtk.TreeModel + iter: gtk.TreeIter pointing at the row + lcolor: list with 2 gtk colors for even and uneven record + + Method connected to "set_cell_data_func" of many column + The set_cell_data_func() method sets the data function (or method) + to use for the column gtk.CellRenderer specified by cell_renderer. + This function (or method) is used instead of the standard attribute + mappings for setting the column values, and should set the attributes + of the cell renderer as appropriate. func may be None to remove the + current data function. The signature of func is: + -def celldatafunction(column, cell, model, iter, user_data) + -def celldatamethod(self, column, cell, model, iter, user_data) + where column is the gtk.TreeViewColumn in the treeview, cell is the + gtk.CellRenderer for column, model is the gtk.TreeModel for the + treeview and iter is the gtk.TreeIter pointing at the row. + + The method sets cell background color for all columns + and text for index and amount columns. + """ + _row_path = tree_model.get_path(iter) + _number = _row_path[-1] + if column is self.__index_column: + cell_renderer.set_property('text', str(_number + 1)) + self.__index_column.get_cell_renderers()[1].set_property( + 'cell-background-gdk', lcolor[_number % 2]) + if self.__treeview.get_cursor() == (_row_path,column): + cell_renderer.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["ACTIVE"])) + else: + cell_renderer.set_property('cell-background-gdk', + lcolor[_number % 2]) + + def _clear(self): + """def _clear(self) + + it deletes the __budget value + this would not be necessary if there were not circular references, + which are pending to fix + """ + del self.__budget + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + def getPage(self): + """def getPage(self) + + return the Page + """ + return self.__page + + def setPage(self,page): + """def setPage(self) + + set the Page + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the Budget objet + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + active_path_record = property(getActivePathRecord, None, None, + "Active path record") + widget = property(getWidget, None, None, + "main widget") + path = property(getPath, setPath, None, + "Path that identifies the item in the page notebook") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + + +class OptionView(object): + """gui.OptionView: + + Description: + It creates a treeview whith the column "Option Name" "Value" + and "Type" to show and edit Options + Constructor: + OptionView(option_list) + option_list: list of options + (option_name, type) + Ancestry: + +-- object + +-- OptionView + Atributes: + "__liststore" + "__treeview" + "__option_column" + "__value_column" + "__type_column" + "__treeselection" + "__widget": Main windget + "__option_list" + "__option_dict" + "__description_label" + "option_types" + "widget": __widget + Methods: + __init__(self, option_list) + createColumn(self, args) + createTextBaseColumn(self,args) + createBaseColumn(self,args) + """ + + def __init__(self, option_list): + """__init__(self, option_list) + + """ + self.__option_dict = {} + self.__option_list = [] + self.option_types = {"boolean" : _("Boolean"), + "integer": _("Integer"), + "string": _("Text"), + "color" : _("Color"), + "list" : _("List")} + # ListStore + self.__liststore = gtk.ListStore(str, str, str, str, str) + # Treeview + self.__treeview = gtk.TreeView(self.__liststore) + self.__treeview.set_enable_search(False) + self.__treeview.set_reorderable(False) + self.__treeview.set_headers_clickable(False) + # vbox + _vbox = gtk.VBox() + # Scrolled_window + _scrolled_window = gtk.ScrolledWindow() + _scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _scrolled_window.add(self.__treeview) + _scrolled_window.show() + _vbox.pack_start(_scrolled_window) + # colors + _text_color = gtk.gdk.color_parse(globals.color["TEXT"]) + _background_color = [ + gtk.gdk.color_parse(globals.color["UNEVEN"]), + gtk.gdk.color_parse(globals.color["EVEN"])] + # Option Column + self.__option_column = gtk.TreeViewColumn() + self.__option_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + self.__option_column.set_fixed_width(150) + self.__option_column.set_resizable(True) + _option_cell = gtk.CellRendererText() + _option_cell.set_property('foreground-gdk', _text_color) + self.__option_column.pack_start(_option_cell, True) + self.__option_column.set_cell_data_func(_option_cell, self.colorCell, + _background_color) + self.__option_column.set_title(_("Option name")) + self.__option_column.add_attribute(_option_cell, 'text', 1) + self.__treeview.append_column(self.__option_column) + # Value Column + self.__value_column = gtk.TreeViewColumn() + self.__value_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + self.__value_column.set_fixed_width(275) + self.__value_column.set_resizable(True) + _value_cell = gtk.CellRendererText() + _value_cell.set_property('foreground-gdk', _text_color) + self.__value_column.pack_start(_value_cell, True) + self.__value_column.set_cell_data_func(_value_cell, self.colorCell, + _background_color) + self.__value_column.set_title(_("Value")) + self.__value_column.add_attribute(_value_cell, 'text', 2) + self.__treeview.append_column(self.__value_column) + # Type Column + self.__type_column = gtk.TreeViewColumn() + self.__type_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + self.__type_column.set_fixed_width(70) + self.__type_column.set_resizable(True) + _type_cell = gtk.CellRendererText() + _type_cell.set_property('foreground-gdk', _text_color) + self.__type_column.pack_start(_type_cell, True) + self.__type_column.set_cell_data_func(_type_cell, self.colorCell, + _background_color) + self.__type_column.set_title(_("Type")) + self.__treeview.append_column(self.__type_column) + # End Column + _end_column = gtk.TreeViewColumn() + _end_column.set_clickable(False) + _end_cell = gtk.CellRendererText() + _end_cell.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["UNEVEN"])) + _end_column.pack_start(_end_cell, True) + self.__treeview.append_column(_end_column) + # Connect + self.__treeview.connect("key-press-event", self.treeviewKeyPressEvent) + self.__treeview.connect("button-press-event", self.treeviewClickedEvent) + # control selection + self.__treeselection = self.__treeview.get_selection() + self.__treeselection.set_mode(gtk.SELECTION_MULTIPLE) + self.__treeselection.set_select_function(self.controlSelection) + # labels + _frame = gtk.Frame() + _frame.set_shadow_type(gtk.SHADOW_OUT) + _vbox2 = gtk.VBox() + _frame.add(_vbox2) + _alignement = gtk.Alignment(xalign=0, yalign=0, xscale=0, yscale=0) + _alignement.set_padding(0, 0, 12, 0) + _label = gtk.Label() + _label.set_markup("<b>" + _("Description:") + "</b>") + _label.show() + _alignement.add(_label) + _alignement.show() + _alignement2 = gtk.Alignment(xalign=0, yalign=0, xscale=0, yscale=0) + _alignement2.set_padding(0, 0, 24, 0) + self.__description_label = gtk.Label() + self.__description_label.show() + _alignement2.add(self.__description_label) + _alignement2.show() + _vbox2.pack_start(_alignement, False) + _vbox2.pack_start(_alignement2, False) + _vbox2.show() + _frame.show() + _vbox.pack_start(_frame, False) + # Show + self.__treeview.show() + _vbox.show() + self.__widget = _vbox + + def treeviewKeyPressEvent(self, widget, event): + """def treeviewKeyPressEvent(self, widget, event) + + widget: treewiew widget + event: Key Press event + Method connected to "key-press-event" signal + The "key-press-event" signal is emitted when the user presses a key + on the keyboard. + Returns :TRUE to stop other handlers from being invoked for the event. + Returns :FALSE to propagate the event further. + + If the user press the right cursor button and the cursor is in the + value column or pres the left cursor button and the cursor is + in the value column the event is estoped, else the event is propagated. + """ + (_cursor_path, _column) = self.__treeview.get_cursor() + if (event.keyval == gtk.keysyms.Right \ + and _column == self.__value_column) \ + or (event.keyval == gtk.keysyms.Left \ + and _column == self.__value_column): + return True + else: + _description = self.__liststore[_cursor_path][4] + self.__description_label.set_text(_description) + return False + + def treeviewClickedEvent(self, widget, event): + """def treeviewClickedEvent(self, widget, event) + + widget: treewiew widget + event: clicked event + Method connected to "button-press-event" signal + The "button-press-event" signal is emitted when a mouse button is + pressed. + Returns TRUE to stop other handlers from being invoked for the event. + Returns FALSE to propagate the event further. + + The cursos is moved to value column. + """ + path_at_pos = self.__treeview.get_path_at_pos(int(event.x), + int(event.y)) + if not path_at_pos is None: + _path_cursor, _column, _x, _y = path_at_pos + _description = self.__liststore[_path_cursor][4] + self.__description_label.set_text(_description) + if _column == self.__value_column: + return False + else: + self.__treeview.set_cursor(_path_cursor,self.__value_column, + True) + self.__treeview.grab_focus() + return True + return True + + def controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + Return False so none row is selected + """ + return False + + + def colorCell(self, column, cell_renderer, tree_model, iter, lcolor): + """def colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + + column: the gtk.TreeViewColumn in the treeview + cell_renderer: a gtk.CellRenderer + tree_model: the gtk.TreeModel + iter: gtk.TreeIter pointing at the row + lcolor: list with 2 gtk colors for even and uneven record + + Method connected to "set_cell_data_func" of the column + The set_cell_data_func() method sets the data function (or method) + to use for the column gtk.CellRenderer specified by cell_renderer. + This function (or method) is used instead of the standard attribute + mappings for setting the column values, and should set the attributes + of the cell renderer as appropriate. func may be None to remove the + current data function. The signature of func is: + -def celldatafunction(column, cell, model, iter, user_data) + -def celldatamethod(self, column, cell, model, iter, user_data) + where column is the gtk.TreeViewColumn in the treeview, cell is the + gtk.CellRenderer for column, model is the gtk.TreeModel for the + treeview and iter is the gtk.TreeIter pointing at the row. + + The method sets cell background color for all columns + and text for type column. + """ + _row_path = tree_model.get_path(iter) + _number = _row_path[-1] + if self.__treeview.get_cursor() == (_row_path,column): + cell_renderer.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["ACTIVE"])) + else: + cell_renderer.set_property('cell-background-gdk', + lcolor[_number % 2]) + if column is self.__type_column: + _type = self.option_types[tree_model[_row_path][3]] + cell_renderer.set_property('text', _type) + + def setOptions(self, option_list): + """setOptions(self, option_list) + + option_list: list of tuples + (option, option name, type) + option: option identifier + option name: a string with the option name + Description: a string with the option description + type: can be "boolean" + "integer" + "string" + "color" + Sets the Options in the treeview rows + """ + self.__option_dict = {} + self.__option_list = [] + self.__liststore.clear() + if isinstance(option_list, list): + for _option in option_list: + if isinstance(_option, tuple) and len(_option) == 4: + _option_key = _option[0] + _option_name = _option[1] + _option_type = _option[2] + _option_description = _option[3] + if isinstance(_option_key, str) and \ + (isinstance(_option_name, str) or\ + isinstance(_option_name, unicode))and \ + _option_type in self.option_types.keys(): + self.__liststore.append([_option_key, _option_name, "", + _option_type, _option_description]) + self.__option_dict[_option_key] = [_option_name, "", + _option_type, _option_description] + self.__option_list.append(_option_key) + else: + print _("Option values must be strings") + else: + print _("Option must be a tuple with 4 items") + else: + print _("Option list must be a list") + return + def setValues(self, values): + """setValues(self, values) + + values: dictionary {option : value} + + Sets the Options values + """ + if isinstance(values, dict): + for _option, _value in values.iteritems(): + if _option in self.__option_dict: + _type = self.__option_dict[_option][2] + if _type == "boolean": + if isinstance(_value, bool): + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + self.__liststore.set_value(_iter, 2, _value) + self.__option_dict[_option][1] = _value + else: + print _("Icorrect type, must be boolean") + elif _type == "integer": + try: + _value = int(_value) + except ValueError: + print _("Icorrect type, must be integer") + else: + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + self.__liststore.set_value(_iter, 2, _value) + self.__option_dict[_option][1] = _value + elif _type == "string": + if isinstance(_value, str): + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + self.__liststore.set_value(_iter, 2, _value) + self.__option_dict[_option][1] = _value + else: + print _("Icorrect type, must be string") + elif _type == "list": + if isinstance(_value, list): + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + _str_value = "" + for _item_value in _value: + _str_value = _str_value + _item_value + "," + if _str_value[-1] == ",": + _str_value = _str_value[:-1] + self.__liststore.set_value(_iter, 2, _str_value) + self.__option_dict[_option][1] = _value + else: + print _("Icorrect type, must be list") + elif _type == "color": + if isinstance(_value, str): + try: + _color = gtk.gdk.color_parse(_value) + except ValueError: + print _("Icorrect type, must be a parseable " \ + "color") + else: + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + self.__liststore.set_value(_iter, 2, _value) + self.__option_dict[_option][1] = _value + else: + print _("Type must be boolean, integer, string or "\ + "color") + else: + print _("Value must be in the option dict") + else: + print _("Values must be a dict") + self.__treeview.set_cursor((0),self.__value_column, False) + self.__treeview.grab_focus() + (_cursor_path, _column) = self.__treeview.get_cursor() + _description = self.__liststore[_cursor_path][4] + self.__description_label.set_text(_description) + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + widget = property(getWidget, None, None, + "The main widget (gtk.ScrolledWindow)") + + +class OpenDurusDatabase(object): + """gui.OpenFiebdc + + Description: + Class to show a dialog to open a durus price database + Constructor: + OpenDurusDatabase() + Ancestry: + +-- object + +-- OpenDurusDatabase + Atributes: + "_openMethod": The method to open the file + "__file": the filename to open + "__window": the File Selection window + Methods: + __init__(self) + setActiveCode(self, code) + """ + def __init__(self, openMethod): + self.__openMethod = openMethod + self.__file = None + self.__window = gtk.FileSelection("Abrir Archivo") + self.__window.connect("destroy", self.destroy) + self.__window.ok_button.connect("clicked", self._openFile) + self.__window.cancel_button.connect("clicked", + lambda w: self.__window.destroy()) + self.__window.set_filename(globals.getHomePath("DURUS-DATABASE")) + self.__window.show() + + def destroy(self, widget): + gtk.main_quit() + + def main(self): + gtk.main() + + def _openFile(self, w): + self.__file = self.__window.get_filename() + _filename = os.path.basename(self.__file) + _filename_list = _filename.split(".") + _filename_ext = _filename_list[-1] + if _filename == "": + print _("No file selected") + elif len(_filename_list) < 2 or _filename_ext != "durus": + print _("The filename must have durus extension") + else: + try: + self.__openMethod(self.__window.get_filename()) + self.__window.destroy() + except IOError: + print utils.mapping("IOError, $1", (self.__file,))