Mercurial > pyarq-presupuestos
view Gtk/gui.py @ 25:189f8274aecd
gui. switch navigation menu on switch page
author | Miguel Ángel Bárcena Rodríguez <miguelangel@obraencurso.es> |
---|---|
date | Mon, 20 May 2019 13:18:33 +0200 |
parents | 65e7ae0d0e63 |
children | 16f91684686b |
line wrap: on
line source
# -*- coding: utf-8 -*- ## File gui.py ## This file is part of pyArq-Presupuestos. ## ## Copyright (C) 2010-2019 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 the measure information 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 # Modules # python 2/3 compatibility from __future__ import absolute_import, division, print_function, unicode_literals # Standar Modules import sys import os import time # gui import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk from gi.repository import GdkPixbuf from gi.repository import Gio from gi.repository import GLib from gi.repository import Gdk from gi.repository import Pango import weakref # pyArq-Presupuestos Modules from Gtk import importFiebdc from Generic import base from Generic import fiebdc from Generic import utils from Generic import globalVars from Generic import openwith # Load default icon _icons = [ "ICON16", "ICON32","ICON64","ICON128"] _pixbufIcons = [] for _icon in _icons: if os.path.exists(globalVars.getAppPath(_icon)): _icon_file = globalVars.getAppPath(_icon) _pixbufIcon = GdkPixbuf.Pixbuf.new_from_file(_icon_file) _pixbufIcons.append(_pixbufIcon) else: print(utils.mapping(_("The icon file does not exist. '$1'"), (str(globalVars.getAppPath(_icon)),)) ) if len(_pixbufIcons) > 0: Gtk.Window.set_default_icon_list(_pixbufIcons) else: print(utils.mapping(_("The icon file does not exist. '$1'"), (str(globalVars.getAppPath("ICON")),)) ) # Autodetect desktop if globalVars.desktop["autodetect"]: openwith.autodetect_desktop() print(utils.mapping(_("pyArq-Presupuestos running on $1"), (globalVars.desktop["desktop"],))) class App(Gtk.Application): """gui.App: Description: This is the Gtk application base class. Constructor: App() Ancestry: +-- Gtk.Application https://lazka.github.io/pgi-docs/Gtk-3.0/classes/Application.html +-- App Atributes: Methods: do_activate do_startup do_open """ def __init__(self, *args, **kwargs): """__init__() Sets the application id and application_name. """ self.filesToOpen = [] self._application_id = "es.obraencurso.pyarq.presupuestos" super(App, self).__init__(application_id=self._application_id, flags=Gio.ApplicationFlags.HANDLES_OPEN, **kwargs) GLib.set_prgname(self._application_id) def do_open(self, files, n_files, hint): """do_open(files, n_files, hint) Set the filename list to open. """ self.filesToOpen = files self.activate() self.filesToOpen = [] return 0 def do_activate(self): """do_activate() Shows new appplication windows. """ _mainWindow = MainWindow(self, self.filesToOpen ) _mainWindow.window.present_with_time(Gtk.get_current_event_time()) def do_startup(self): """do_startup() Sets the app actions. """ Gtk.Application.do_startup(self) # App Actions action = Gio.SimpleAction.new("newWindow", None) action.connect("activate", self._on_newWindow) self.add_action(action) action = Gio.SimpleAction.new("acell_newWindow", None) action.connect("activate", self._on_control_n) self.add_action(action) action = Gio.SimpleAction.new("about", None) action.connect("activate", self._on_about) self.add_action(action) action = Gio.SimpleAction.new("quit", None) action.connect("activate", self._on_quit) self.add_action(action) action = Gio.SimpleAction.new("acell_quit", None) action.connect("activate", self._on_control_q) self.add_action(action) # App menu _app_menu = Gio.Menu() _section_window = Gio.Menu() _section_window.append(_("_New window"), "app.newWindow") _section_window.append(_("_Close window"), "win.CloseWindow") _app_menu.append_section(None,_section_window) self.set_accels_for_action('win.acell_close', ["<Primary>x"]) self.set_accels_for_action('app.acell_newWindow', ["<Primary>n"]) _section_general = Gio.Menu() _section_general.append(_("About") + " " + globalVars.name, "app.about") _app_menu.append_section(None,_section_general) _section_quit = Gio.Menu() _section_quit.append(_("_Quit application"), "app.quit") self.set_accels_for_action('app.acell_quit', ["<Primary>q"]) _app_menu.append_section(None,_section_quit) self.set_app_menu(_app_menu) # TODO : from gui config win_menu = False # Win Menu if win_menu: _win_menu = Gio.Menu() _win_submenu_file = Gio.Menu.new() _import_fiebdc = Gio.MenuItem.new(_("_Import Fiebdc"), "win.ImportFiebdc") _import_fiebdc.set_icon(Gio.Icon.new_for_string("document-open")) _win_submenu_file.append_item(_import_fiebdc) _close_tab = Gio.MenuItem.new(_("_Close tab"), "win.CloseTab") _close_tab.set_icon(Gio.Icon.new_for_string("window-close")) _win_submenu_file.append_item(_close_tab) _win_menu.append_submenu(_("_File"), _win_submenu_file) _win_submenu_go = Gio.Menu.new() _back = Gio.MenuItem.new(_("_Back"), "win.GoPrevious") _back.set_icon(Gio.Icon.new_for_string("go-previous")) _win_submenu_go.append_item(_back) _forward = Gio.MenuItem.new(_("_Forward"), "win.GoPosterior") _forward.set_icon(Gio.Icon.new_for_string("go-next")) _win_submenu_go.append_item(_forward) _up = Gio.MenuItem.new(_("_Up Item"), "win.GoUp") _up.set_icon(Gio.Icon.new_for_string("go-up")) _win_submenu_go.append_item(_up) _root = Gio.MenuItem.new(_("_Root"), "win.GoToRoot") _root.set_icon(Gio.Icon.new_for_string("go-top")) _win_submenu_go.append_item(_root) _win_menu.append_submenu(_("_Go"), _win_submenu_go) self.set_menubar(_win_menu) def _on_newWindow(self, action, param): """on_newWindow(action, param) Shows new appplication windows. """ _mainWindow = MainWindow(self, []) _mainWindow.window.present_with_time(Gtk.get_current_event_time()) def _on_about(self, action, param): """_on_about(action, param) Shows About dialog. """ _aboutDialog = Gtk.AboutDialog(modal=False) _aboutDialog.set_program_name(globalVars.name) _aboutDialog.set_copyright(base.copyright) _aboutDialog.set_authors(base.authors) _aboutDialog.set_version(globalVars.version + globalVars.changeset) _aboutDialog.set_website(base.website) _aboutDialog.set_website_label(base.website_label) _aboutDialog.set_license_type(Gtk.License(3)) _aboutDialog.set_comments(base.comments) _aboutDialog.connect("response", self._on_close_aboutdialog) _aboutDialog.present_with_time(Gtk.get_current_event_time()) def _on_close_aboutdialog(self, action, parameter): """on_close_aboutdialog(action, param) Close About dialog. """ action.destroy() def _on_control_q(self, action, param): """on_control_q(action, param) Quit app. """ print("Control q -> Quit app") self.quit() def _on_quit(self, action, param): """_on_quit(action, param) Quit app. """ self.quit() def _on_control_n(self, action, param): """on_control_n(action, param) Shows new appplication windows. """ print("Control n -> New window") self._on_newWindow(action, param) class MainWindow(object): """gui.MainWindow: Description: Creates and shows the main window. This is the interface base class. Constructor: MainWindow(app, files) Ancestry: +-- object +-- MainWindow Atributes: self.window: Gtk.ApplicationWindow object Methods: changeHistorySignal changeActiveSignal appendEmptyPage updatePage closePage """ # TODO:* Can choose open budget in new window # TODO:* Can choose show more than one notebook in the same window or # TODO: can show basedata notebook in a side pane def __init__(self, app, files): """__init__(app, files) Initialize the atributes self.__page_list without data. Creates the widgets "window" and "__notebook". app: Gtk.Application instance files: Gio.file list from command line self.window: Gtk.ApplicationWindow object self.__page_list: List of pages ("Page" object) self.__notebook: Notebook widget ("Gtk.Notebook" object) self.__general_action_group: "General" action group self.__navigation_action_group: "Navigation" action group self.__navigation_is_enabled: True/False self.__goBack_button """ self.__page_list = [] # Main window self.window = Gtk.ApplicationWindow(application=app) self.window.set_default_size(771, 570) self.window.set_title("Presupuestos") self.window.set_border_width(5) self.window.connect("delete_event", self._delete_event) # HeaderBar _hb = Gtk.HeaderBar() _hb.set_show_close_button(True) _hb.props.title = "Presupuestos" self.window.set_titlebar(_hb) _hb.show() # Actions # General Actions self.__general_action_group = Gio.SimpleActionGroup.new() # CloseWindow Action _action = Gio.SimpleAction.new("CloseWindow", None) _action.connect("activate", self._menuitemClose) self.window.add_action(_action) self.__general_action_group.insert(_action) # CloseWindow from acell Action _action = Gio.SimpleAction.new("acell_close", None) _action.connect("activate", self._on_control_x) self.window.add_action(_action) self.__general_action_group.insert(_action) # ImportFiebdc Action _action = Gio.SimpleAction.new("ImportFiebdc", None) _action.connect("activate", self._on_menuitemImportFiebdc) self.window.add_action(_action) self.__general_action_group.insert(_action) # CloseTab action self.__closeTab_action = Gio.SimpleAction.new("CloseTab", None) self.__closeTab_action.connect("activate", self._on_CloseTab) self.window.add_action(self.__closeTab_action) self.__general_action_group.insert(self.__closeTab_action) # Navigation Actions self.__navigation_is_enabled = False self.__navigation_action_group = Gio.SimpleActionGroup.new() # Go Previous action self.__GoPrevious_action = Gio.SimpleAction.new("GoPrevious", None) self.__GoPrevious_action.connect("activate", self._menuitemGoPrevious) self.window.add_action(self.__GoPrevious_action) self.__general_action_group.insert(self.__GoPrevious_action) self.__GoPrevious_action.set_enabled(False) # Go posterior action self.__GoPosterior_action = Gio.SimpleAction.new("GoPosterior", None) self.__GoPosterior_action.connect("activate", self._menuitemGoPosterior) self.window.add_action(self.__GoPosterior_action) self.__general_action_group.insert(self.__GoPosterior_action) self.__GoPosterior_action.set_enabled(False) # Go Up action self.__GoUp_action = Gio.SimpleAction.new("GoUp", None) self.__GoUp_action.connect("activate", self._menuitemGoUp) self.window.add_action(self.__GoUp_action) self.__general_action_group.insert(self.__GoUp_action) self.__GoUp_action.set_enabled(False) # Go to Root action self.__GoToRoot_action = Gio.SimpleAction.new("GoToRoot", None) self.__GoToRoot_action.connect("activate", self._menuitemGoToRoot) self.window.add_action(self.__GoToRoot_action) self.__general_action_group.insert(self.__GoToRoot_action) self.__GoToRoot_action.set_enabled(False) # Vertical Grid _vbox1 = Gtk.Grid() _vbox1.set_orientation(Gtk.Orientation(1)) # 1 Vertical self.window.add(_vbox1) _vbox1.show() # Toolbar _toolbar = Gtk.Toolbar() _toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR) # Import Fiebdc _gtk_image = Gtk.Image.new_from_icon_name("document-open",1) _gtk_image.show() _ImportFiebdc_button = Gtk.ToolButton.new(_gtk_image, _("Import Fiebdc")) _ImportFiebdc_button.set_tooltip_text(_("Import Fiebdc")) _ImportFiebdc_button.set_is_important(False) # label not shown _toolbar.insert(_ImportFiebdc_button, -1) _ImportFiebdc_button.show() _ImportFiebdc_button.set_action_name("win.ImportFiebdc") # Close tab _gtk_image = Gtk.Image.new_from_icon_name("window-close",1) _gtk_image.show() _closeTabButton = Gtk.ToolButton.new(_gtk_image , _("_Close tab")) _closeTabButton.set_tooltip_text(_("Close tab")) _closeTabButton.set_is_important(False) # label not shown _toolbar.insert(_closeTabButton, -1) _closeTabButton.show() _closeTabButton.set_action_name("win.CloseTab") # Separator item _separator = Gtk.SeparatorToolItem() _separator.show() _toolbar.insert(_separator, -1) # Go Back _gtk_image = Gtk.Image.new_from_icon_name("go-previous",1) _gtk_image.show() self.__goBack_button = Gtk.MenuToolButton.new(_gtk_image ,_("Back")) self.__goBack_button.set_tooltip_text(_("Back")) self.__goBack_button.set_is_important(False) # label not shown _toolbar.insert(self.__goBack_button, -1) self.__goBack_button.show() self.__goBack_button.set_action_name("win.GoPrevious") # Go Forward _gtk_image = Gtk.Image.new_from_icon_name("go-next",1) _gtk_image.show() self.__goForward_button = Gtk.MenuToolButton.new(_gtk_image, _("Forward")) self.__goForward_button.set_tooltip_text(_("Forward")) self.__goForward_button.set_is_important(False) # label not shown _toolbar.insert(self.__goForward_button, -1) self.__goForward_button.show() self.__goForward_button.set_action_name("win.GoPosterior") # Go Up Item _gtk_image = Gtk.Image.new_from_icon_name("go-up",1) _gtk_image.show() _goUP_button = Gtk.ToolButton.new(_gtk_image ,_("Up Item")) _goUP_button.set_tooltip_text(_("Up Item")) _goUP_button.set_is_important(False) # label not shown _toolbar.insert(_goUP_button, -1) _goUP_button.show() _goUP_button.set_action_name("win.GoUp") # Go Root Item _gtk_image = Gtk.Image.new_from_icon_name("go-top",1) _gtk_image.show() _goRoot_button = Gtk.ToolButton.new(_gtk_image ,_("Root")) _goRoot_button.set_tooltip_text(_("Root")) _goRoot_button.set_is_important(False) # label not shown _toolbar.insert(_goRoot_button, -1) _goRoot_button.show() _goRoot_button.set_action_name("win.GoToRoot") # Pack and show _toolbar.set_hexpand(True) # with extra horizontal space _toolbar.show() _vbox1.add(_toolbar) # Notebook self.__notebook = Gtk.Notebook() _vbox1.add(self.__notebook) self.__notebook.set_tab_pos(Gtk.PositionType(2)) # Up self.__notebook.set_property("expand", True) # widget expand all space self.__notebook.set_show_tabs(True) self.__notebook.set_show_border(True) self.__notebook.set_scrollable(True) self.__notebook.connect("switch-page", self._switch_page) self.__notebook.show() self._main() if len(files) > 0: for file in files: _budget = base.Budget() _budget_file = fiebdc.Read() _read_method = _budget_file.readFile _filename = file.get_path() _filetype = "budget" _exit_method = _budget_file.cancel _file_window = FileSelectionWindow(self, _read_method, _budget, _filename, _exit_method, _filetype) def changeHistorySignal(self): """changeHistorySignal() A pane emit the updateHistory signal. Nothing to do now """ pass def changeActiveSignal(self): """changeActiveSignal() A pane emit the change-active signal. Chech buttons sensitive """ self._checkButtonsSensitive(self.__notebook.get_current_page()) def _checkButtonsSensitive(self, page_num): """_checkButtonsSensitive(page_num) page_num: page number in notebook Check and if necessary update the enabled state of the navigation buttons. """ _page = self.__page_list[page_num] if isinstance(_page, Page) and \ self.__navigation_is_enabled: # GoToRoot and GoUp actions _goto_root = self.__GoToRoot_action _go_up = self.__GoUp_action if len(_page.activePathRecord) == 1 and \ _goto_root.get_enabled(): _goto_root.set_enabled(False) _go_up.set_enabled(False) elif len(_page.activePathRecord) != 1 and \ not _goto_root.get_enabled(): _goto_root.set_enabled(True) _go_up.set_enabled(True) # GoPrevMenu action _go_Previous = self.__GoPrevious_action _go_prev = self.__GoPrevious_action if _page.previousPathRecord is None: if _go_prev.get_enabled(): _go_prev.set_enabled(False) self.__goBack_button.props.menu = None else: if not _go_prev.get_enabled(): _go_prev.set_enabled(True) self.__goBack_button.set_menu(_page.back_menu) # GoPostMenu action _go_Posterior = self.__GoPosterior_action if _page.posteriorPathRecord is None: if _go_Posterior.get_enabled(): _go_Posterior.set_enabled(False) self.__goForward_button.props.menu = None else: if not _go_Posterior.get_enabled(): _go_Posterior.set_enabled(True) self.__goForward_button.set_menu(_page.forward_menu) def _disable_navigation(self): self.__GoPrevious_action.set_enabled(False) self.__GoPosterior_action.set_enabled(False) self.__GoUp_action.set_enabled(False) self.__GoToRoot_action.set_enabled(False) self.__navigation_is_enabled = False def _switch_page(self, notebook, page, page_num): """_switch_page(notebook, page, page_num) Method connected to the "switch-page" signal of the notebook widget It shows/hides closeTabButton and changes the sensitive state of the navigation action group """ for _page_num, _page in enumerate(self.__page_list): if _page_num == page_num: _page.closeTabButton.show() else: _page.closeTabButton.hide() _page = self.__page_list[page_num] if isinstance(_page, EmptyPage) and \ self.__navigation_is_enabled: self._disable_navigation() elif isinstance(_page, Page): if not self.__navigation_is_enabled: self.__navigation_is_enabled = True self.__goBack_button.set_menu(_page.back_menu) self.__goForward_button.set_menu(_page.forward_menu) self._checkButtonsSensitive(page_num) def _main(self): """main() Shows window. """ self.window.show() def appendEmptyPage(self, emptyPage): """appendEmptyPage(emptyPage) Append a empty page to the notebook. """ self.__page_list.append(emptyPage) self.__notebook.append_page(emptyPage.widget, emptyPage.title) # TODO: reordenable and detachable Pages #self.__notebook.set_tab_reorderable(emptyPage.widget, True) #self.__notebook.set_tab_detachable(emptyPage.widget, True) def updatePage(self, empty_page, page): """updatePage(page) Update emptyPage to Page. """ _page_num = self.__notebook.page_num(empty_page.widget) self.__page_list[_page_num] = page if self.__notebook.get_current_page() == _page_num: if not self.__navigation_is_enabled: self.__navigation_is_enabled = True self._checkButtonsSensitive(_page_num) def _on_control_x(self, action, param): print("Control x -> Close window") self.window.destroy() def _on_menuitemImportFiebdc(self, action, parameter): """_on_menuitemImportFiebdc(action, parameter) action: the action where the event is emitted from parameter: None Callback to open a budget file. Creates and shows a file selection window to open a budget file. """ _budget = base.Budget() _budget_file = fiebdc.Read() _read_method = _budget_file.readFile _filename = "" _filetype = "budget" _exit_method = _budget_file.cancel _file_window = FileSelectionWindow(self, _read_method, _budget, _filename, _exit_method, _filetype) def _menuitemClose(self, action, parameter): """_menuitemClose(action, parameter) action: the action where the event is emitted from parameter: None Callback to close the main window. """ self.window.destroy() def _on_CloseTab(self, action, parameter): """_on_CloseTab(action, parameter) action: the action where the event is emitted from parameter: None Callback to close tab. """ _page_num = self.__notebook.get_current_page() if _page_num != -1: _page = self.__page_list[_page_num] _page.close() def closePage(self, page): """closePage(page) page: EmptyPage or Page object Removes a page from notebook and page_list. Hide navigation action group if necessary """ if page in self.__page_list: _page_num = self.__page_list.index(page) self.__page_list.pop(_page_num) page.clear() self.__notebook.remove_page(_page_num) if len(self.__page_list) == 0: self._disable_navigation() else: raise IndexError( _("The page is not in the page list") ) def _menuitemGoToRoot(self, action, parameter): """_menuitemGoToRoot(action, parameter) action: the action where the event is emitted from parameter: None Callback to go to root record. """ _page_num = self.__notebook.get_current_page() if _page_num == -1: return _page = self.__page_list[_page_num] if isinstance(_page, Page): # not loading budget _page.propagateMessageFrom("change_active", (-1,), (0,)) def _menuitemGoUp(self, action, parameter): """_menuitemGoUp(action, parameter) action: the action where the event is emitted from parameter: None Callback to go to up record. """ _page_num = self.__notebook.get_current_page() if _page_num != -1: _page = self.__page_list[_page_num] if isinstance(_page, Page): # not loading budget _active_path = _page.activePathRecord if len(_active_path) > 1: _budget = _page.budget _up_path = _active_path[:-1] if _budget.hasPath(_up_path): _page.propagateMessageFrom("change_active", (-1,), _up_path) def _menuitemGoPrevious(self, action, parameter): """_menuitemGoPrevious(action, parameter) action: the action where the event is emitted from parameter: None Callback to go to previous record. """ _page_num = self.__notebook.get_current_page() if _page_num != -1: _page = self.__page_list[_page_num] if isinstance(_page, Page): # not loading budget _previous_path = _page.previousPathRecord if _previous_path is not None: _budget = _page.budget if _budget.hasPath(_previous_path): _page.propagateMessageFrom("change_active", (-1,), _previous_path) def _menuitemGoPosterior(self, action, parameter): """_menuitemPosterior(action, parameter) action: the action where the event is emitted from parameter: None Callback to go to posterior record. """ _page_num = self.__notebook.get_current_page() if _page_num != -1: _page = self.__page_list[_page_num] if isinstance(_page, Page): # not loading budget _posterior_path = _page.posteriorPathRecord if _posterior_path is not None: _budget = _page.budget if _budget.hasPath(_posterior_path): _page.propagateMessageFrom("change_active", (-1,), _posterior_path) def _delete_event(self, widget, event): """_delete_event(widget, event) widget: the widget where the event is emitted from event: the "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 class FileSelectionWindow(object): """gui.FileSelectionWindow: Description: Class to show the selection file window Constructor: FileSelectionWindow(mainWindow, readFileMethod, budget, filename, cancelMethod, filetype) Ancestry: +-- object +-- FileSelectionWindow Atributes: No public Atributes Methods: No public Methods """ def __init__(self, mainWindow, readFileMethod, budget, filename, cancelMethod, filetype): """__init__(mainWindow, readFileMethod, budget, filename, cancelMethod, filetype) mainWindow: MainWindow object readFileMethod: Method to read the selected file budget: base.Budget object filename: "file" cancelMethod: Method to cancel the read method fileytpe: "budget" or "database". Sets the init atributes, creates the file selection window Connects the events: * clicked ok button: _openFile * clicked cancel button: destroy window * destroy event: _destroy """ # TODO: Add file filter self.__mainWindow = mainWindow self.__readFileMethod = readFileMethod self.__budget = budget self.__filetype = filetype self.__cancelMethod = cancelMethod self.__file = None self.__filename = filename if self.__filename == "": self.__dialog = Gtk.FileChooserNative.new(_("Open File"), mainWindow.window, Gtk.FileChooserAction.OPEN, _("Open File"), _("Cancel") ) self.__dialog.set_current_folder(globalVars.getHomePath("BUDGET")) _response = self.__dialog.run() if _response in [ Gtk.ResponseType.OK, Gtk.ResponseType.ACCEPT, Gtk.ResponseType.YES, Gtk.ResponseType.APPLY]: self._openFile(self.__dialog.get_filename()) elif _response == Gtk.ResponseType.CANCEL: self.__dialog.destroy() else: self.__dialog = None self._openFile(self.__filename) def _launchProgressWindow(self, file): """_launchProgressWindow(file) Launch the progress window """ self.__filename = file _emptyPage = EmptyPage(self.__mainWindow, self.__readFileMethod, self.__budget, self.__filename, self.__cancelMethod, self.__filetype) self.__mainWindow.appendEmptyPage(_emptyPage) _emptyPage.run() def _openFile(self, filename): """_openFile(filename) filename: the filename to open If the selected file has a bc3 extension _launchProgressWindow is called """ _file = filename if sys.getfilesystemencoding(): _file = _file.decode(sys.getfilesystemencoding()) self.__file = _file _filename = os.path.basename(self.__file) _filename_ext = _filename.split(".")[-1] if (self.__filetype == "budget" or self.__filetype == "database") and \ _filename_ext != "bc3" and _filename_ext != "BC3": print(_("The file must have 'bc3' extension") ) else: if self.__dialog is not None: self.__dialog.destroy() # TODO: the file exits? is it not binary?, can it be readed? self._launchProgressWindow(self.__file) class EmptyPage(object): """gui.EmptyPage: Description: It creates and shows a page in the notebook while a budget is loaded. The page show the pyarq logo, loading time and a progress bar. Constructor: EmptyPage(mainWindow, readFileMethod, budget, filename, cancelMethod, filetype): mainWindow: Mainwindow object readFileMethod: Method to read the selected file budget: base.Budget object filename: "file" cancelMethod: Method to cancel the read method filetype: "budget", "database" Ancestry: +-- object +-- EmptyPage Atributes: widget: Read. Main widget showed in the pane title: Read. Page Title filetype: Read. budget or basedata endSuccessfully: Read-Write. False/True Methods: run readFile_progress readFile_send_message readFile_set_statistics readFile_end readFile_cancel stopLoading updateGui threadFinishedSignal threadCanceled on_close_button close clear """ def __init__(self, mainWindow, readFileMethod, budget, filename, cancelMethod, filetype): """__init__(mainWindow, readFileMethod, budget, filename, cancelMethod, filetype) mainWindow: Mainwindow object readFileMethod: Method to read the selected file budget: base.Budget object filename: "file" cancelMethod: Method to cancel the read method filetype: "budget" or "database" self.__mainWindow: Mainwindow object self.endSuccessfully False/True self.__readFileMethod: Method to read the selected file self.__budget: base.Budget object self.__filename: "file" self.__cancelMethod: Method to cancel the read method self.__filetype: "budget" or "database" self.__children: the read thread self.__progress: 0 to 1 progress self.__statistics: record statistics self.__widget: main widget, a vertial Gtk.Grid object self.__main_item: None self.__throbber: Gtk.Spinner() self.__budget_icon: Gtk.Image() self.__title: a horizontal Gtk.Grid self.__statusbar: a Gtk.Statusbar self.__statuscontext: the statusbar context self.__progress_bar: a Gtk.ProgressBar """ self.__mainWindow = mainWindow self.endSuccessfully = False self.__readFileMethod = readFileMethod self.__budget = budget self.__filename = filename self.__filetype = filetype self.__cancelMethod = cancelMethod self.__children = None self.__cancel = [False, False] self.__progress = 0.0 self.__statistics = None self.__widget = Gtk.Grid() self.__widget.set_orientation(Gtk.Orientation(1)) # 1 Vertical self.__main_item = None self.__widget.show() # Throbber self.__throbber = Gtk.Spinner() self.__throbber.show() self.__budget_icon = Gtk.Image() _budget_pixbuf = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("BUDGET-ICON")) self.__budget_icon.set_from_pixbuf(_budget_pixbuf) _filename = os.path.basename(filename) _rootfilename = os.path.splitext(_filename)[0] if not _rootfilename == "": _filename = _rootfilename if len(_filename) > 28: _titleLabel = Gtk.Label(_filename[:25] + "...") _titleLabel.set_tooltip_text(_filename) else: _titleLabel = Gtk.Label(_filename) _titleLabel.show() self.__title = Gtk.Grid() self.__title.set_column_spacing(4) self.__title.set_orientation(Gtk.Orientation(0)) # 0 Horizontal self.__title.add(self.__budget_icon) self.__title.add(self.__throbber) self.__title.add(_titleLabel) # Close tab _gtk_image = Gtk.Image() _gtk__pixbuf = Gtk.IconTheme.get_default().load_icon( "window-close-symbolic", 16, 0) _gtk_image.set_from_pixbuf(_gtk__pixbuf) _gtk_image.show() self.closeTabButton = Gtk.ToolButton.new(_gtk_image , _("_Close tab")) self.closeTabButton.set_tooltip_text(_("Close tab")) self.closeTabButton.set_is_important(False) # label not shown self.__title.add(self.closeTabButton) self.closeTabButton.hide() self.__closeTabId = self.closeTabButton.connect("clicked", self.on_close_button) # Statusbar self.__statusbar = Gtk.Statusbar() self.__statuscontext = self.__statusbar.get_context_id("Statusbar") self.__statusbar.show() _iconVbox = Gtk.Grid() _iconVbox.set_orientation(Gtk.Orientation(1)) # 1 Vertical _iconVbox.props.halign = Gtk.Align(3) # 3 Center _iconVbox.props.valign = Gtk.Align(3) # 3 Center _iconVbox.set_property("expand", True) # widget expand all space _pyArqIcon = Gtk.Image() _pyArqIcon.set_from_file(globalVars.getAppPath("PYARQ-ICON")) _pyArqIcon.show() _iconVbox.add(_pyArqIcon) _link = Gtk.LinkButton.new("http://pyarq.obraencurso.es") _iconVbox.add(_link) _link.show() _iconVbox.show() self.__widget.add(_iconVbox) self.__progress_bar = Gtk.ProgressBar() self.__progress_bar.set_show_text(True) self.__progress_bar.show() self.__statusbar.pack_start(self.__progress_bar, False, False, 0) self.__widget.add(self.__statusbar) self.__main_item = None def run(self): """run() Launch clildren and timeouts """ self.__statusbar.push(self.__statuscontext, _("Time: 0s")) self.__throbber.start() self._launchChildren() self._launchTimeout() def readFile_progress(self, percent): """readFile_progress(percent) percent: Percentage executed. Sets progress """ _progress = str(int(round(100 * percent,0))) self.__progress = percent def readFile_send_message(self, message): """readFile_send_message(message) message: mesage from readFile method print( message ) """ print( message ) def readFile_set_statistics(self, statistics): """readFile_set_statistics(statistics) statistics: record statistics from readFile method sets record statistics """ self.__statistics = statistics def readFile_end(self): """readFile_end() The readFile method end successfully """ self.endSuccessfully = True print( self.__statistics.str() ) def readFile_cancel(self): """readFile_cancel() The readFile method is canceled """ self.endSuccessfully = False print(_("Process terminated") ) def stopLoading(self): """stopLoading() Stop progressbar """ self.__throbber.destroy() self.__budget_icon.show() self.__progress_bar.hide() self.__statusbar.pop(self.__statuscontext) def _launchChildren(self): """_launchChildren() 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.__filetype) self.__children.start() def _launchTimeout(self): """_launchTimeout() Launch the timeouts: 1- update progress bar 2- update time label 3- If the other timetouts are stoped the window is closed """ GLib.timeout_add(1000, self._updateLabel, time.time()) GLib.timeout_add(500, self._updateProgressBar) self.__cancel = [False, False] GLib.timeout_add(1000, self._autoClose) def updateGui(self): while Gtk.events_pending(): Gtk.main_iteration() def _updateProgressBar(self): """_updateProgressBar() 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): """_updateLabel(_time) 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): """_autoClose() If the time label and progress bar timeouts are stoped the window is closed and ist tiemeout is stoped """ #_autoClose timeout do nothig if self.__cancel == [ True, True ]: return False else: return True def threadFinishedSignal(self, budget): """threadFinishedSignal(budget) Sets the self.__children to None This causes that the timeouts is ended. This method is called from thread when it finish """ self.__budget = budget self.__children = None self.stopLoading() _page = Page(self.__mainWindow, self.__budget, self.closeTabButton) _children = self.__widget.get_children() for _child in _children: self.__widget.remove(_child) self.__widget.add(_page.widget) self.closeTabButton.disconnect(self.__closeTabId) self.closeTabButton.connect("clicked", _page.on_close_button) self.__mainWindow.updatePage(self, _page) def threadCanceled(self): """threadCanceled() Sets the __children atribute to None This causes that the timeouts is ended. This method is called from thread when is canceled """ self.__children = None self.stopLoading() self.__mainWindow.closePage(self) def on_close_button(self, widget): """on_close_button() Call close """ self.close() def close(self): """close() Close page canceling children """ self.__children.cancel() def clear(self): """clear() Nothig to do now """ pass def _getWidget(self): """_getWidget() Return de main widget to show in the page """ return self.__widget def _getTitle(self): """_getTitle() Return the title of the page, a Gtk.Label objetc """ return self.__title def _getFiletype(self): """_getFiletipe() Return the title of the page, a Gtk.Label objetc """ return self.__filetype widget = property(_getWidget, None, None, "Main widget showed in the pane") title = property(_getTitle, None, None, "Page Title") filetype = property(_getFiletype, None, None, "Filetype: budget or basedata") 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: Page(mainWindow, budget, closeTabButton, active_code=None) mainwindow: MainWindow object budget: base.Budget object closeTabButton. button winget to close tab active_code: Active record code Ancestry: +-- object +-- Page Atributes: widget: Read. Notebook page Widget. (a vertical Gtk.Grid instance) 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 title: Read. Notebook page title (Gtk.Label object) activePathRecord: Read. The active path record previousPathRecord: Read. The previous path record posteriorPathRecord Read. The posterior path record back_menu: back menu to show in menutoolbutton forward_menu: forward menu to show in menutoolbutton Methods: propagateMessageFrom sendMessageTo on_close_button close clear getItem """ # 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, mainWindow, budget, closeTabButton, path_record=None): """__init__(mainWindow, budget, closeTabButton, path_record=None) mainWindow: MainWindow object budget: "base.Budget" object closeTabButton: Button widget to close tab path_record: the active path record self.__mainWindow: MainWindow object self.__widget: a Gtk.Grid self.__panes_list: self.__main_item: Main item in Page. (Paned object or View object) self.closeTabButton: : Button widget to close tab self.__active_path_record: self.__history_back: self.__history_forward: self.__back_menu: a Gtk.Menu self.__forward_menu: a Gtk.Menu """ if path_record is None: path_record = (0,) self.__mainWindow = mainWindow self.closeTabButton = closeTabButton self.__widget = Gtk.Grid() self.__widget.set_orientation(Gtk.Orientation(1)) # 1 Vertical self.__widget.props.margin = 5 #TODO: __panes_list should come from gui config file... # "DecompositionList" "RecordDescription" "Measure" # "Sheet of Conditions" "FileView" "CompanyView" self.__panes_list = [ "v", "DecompositionList", [ "v", "Measure", "RecordDescription" ]] self.__main_item = None self.__active_path_record = () self.__history_back = [] self.__history_forward = [] self.__back_menu = Gtk.Menu() self.__back_menu.show() self.__forward_menu = Gtk.Menu() self.__forward_menu.show() self.budget = budget # Create all items and widgets self._setActivePathRecord(path_record) self.__widget.show() self.__budget_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("BUDGET-ICON")) self.__chapter_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("CHAPTER-ICON")) self.__unit_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("UNIT-ICON") ) self.__material_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("MATERIAL-ICON") ) self.__machinery_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("MACHINERY-ICON")) self.__labourforce_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("LABOURFORCE-ICON")) def propagateMessageFrom(self, message, pane_path, arg=None): """propagateMessageFrom(message, pane_path, arg=None) message: string message * "change_active": change active code * "autoclose" * "split h" * "split v" pane_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, pane_path, arg) self._setActivePathRecord(arg) self.__mainWindow.changeActiveSignal() # Check sensitive buttons elif message == "autoclose": self._closeItem(pane_path) elif message == "split h": self._splitItem(pane_path, "h") elif message == "split v": self._splitItem(pane_path, "v") def sendMessageTo(self, pane, message, pane_path, arg=None): """sendMessageTo(pane, message, pane_path, arg=None) pane: the receiver pane message: string message pane_path: tuple that represents the pane pane_path which emits the message arg: arguments for the message Sends a message to a pane """ pane.runMessage(message, pane_path, arg) def on_close_button(self, widget): self.close() def close(self): """close() Close Page """ self.__mainWindow.closePage(self) def clear(self): """clear() 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, pane_path): """getItem(pane_path) Return the item whith the path "pane_path", it can return a Paned instance or a View instance """ _item = self.__main_item if len(pane_path) == 1: return _item else: return _item.getItem(pane_path[1:]) def _setMainItem(self, item): """_setMainItem(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.add(_main_widget) def _splitItem(self, pane_path, orientation): """_splitItem(pane_path, orientation) Splits the item that is identifies by the pane_path and the orientation """ _item = self.getItem(pane_path) _item.pane_path = pane_path + (0,) _item_clone0 = _item.getClone(pane_path + (0,)) _item_clone1 = _item.getClone(pane_path + (1,)) _paned = Paned(orientation, pane_path, _item_clone0, _item_clone1) if len(pane_path) > 1: _parent = self.getItem(pane_path[:-1]) _parent.setItem(pane_path[-1], [_paned]) else: self._setMainItem(_paned) def _closeItem(self, pane_path): """_closeItem(pane_path) Closes the item that is identifies by the pane_path """ _item = self.getItem(pane_path) if len(pane_path) > 1: # There are more than one item _parent = self.getItem(pane_path[:-1]) _brothers = [ _brother for _brother in _parent] _brothers.remove(_item) _brother = _brothers[0] _parent.widget.remove(_brother.widget) _brother.pane_path = pane_path[:-1] if len(pane_path) > 2: _grandparent = self.getItem(pane_path[:-2]) _grandparent.setItem(pane_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, pane_path=None): """_itemsFactory(list_paned, pane_path=None) 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" pane_path: tuple that represents the item path in the page Creates the items and widgets and returns the main item """ if pane_path is None: pane_path = (0,) 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],pane_path + (0,)) _item2 = self._itemsFactory(list_paned[2],pane_path + (1,)) _item = Paned(list_paned[0], pane_path, _item1, _item2) elif list_paned[0] == "DecompositionList": _item = View( "DecompositionList", self.__budget, weakref.ref(self), pane_path, self.__active_path_record, True) elif list_paned[0] == "RecordDescription": _item = View( "RecordDescription", self.__budget,weakref.ref(self), pane_path, self.__active_path_record, True) elif list_paned[0] == "Measure": _item = View("Measure", self.__budget, weakref.ref(self), pane_path, self.__active_path_record, True) elif list_paned[0] == "Sheet of Conditions": _item = View("Sheet of Conditions", self.__budget, weakref.ref(self), pane_path, self.__active_path_record) elif list_paned[0] == "FileView": _item = View("FileView", self.__budget, weakref.ref(self), pane_path, self.__active_path_record) elif list_paned[0] == "CompanyView": _item = View("CompanyView", self.__budget, weakref.ref(self), pane_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): """_setActivePathRecord(path_record) path_record: the active record path Sets the active record path """ if path_record != self.__active_path_record: if self.__budget.hasPath(path_record): self.__active_path_record = path_record self._appendHistory(path_record) else: raise ValueError( utils.mapping(_("The budget does not have "\ "the path record: $1"), (str(path_record),)) ) def _appendHistory(self, path): """_appendHistory(path)) path: the new active path record Append the new active path record to history lists and update menus """ if len(self.__history_back) > 1 and path in self.__history_back[:-1]: # the new active record is in back history list # then append forward history and pop back history _items_num = len(self.__history_back) - \ self.__history_back.index(path) -1 for _item in range(_items_num): _record_path = self.__history_back.pop() _first_menuitem = self.__back_menu.get_children()[0] self.__back_menu.remove(_first_menuitem) self.__history_forward.append(_record_path) _menuitem = self._menuItemFactory(_record_path) _menuitem.connect_object("activate", self._menuHistoryForward, _record_path, _menuitem) self.__forward_menu.prepend(_menuitem) while len(self.__history_forward) > 100: # list too long self.__history_forward.pop(0) _last_menuitem = self.__forward_menu.get_children()[-1] self.__forward_menu.remove(_last_menuitem) else: # the new active record not is in back history list if len(self.__history_forward) > 1 and \ path in self.__history_forward: # the new active record is in history forward list # then append back history and pop forward history _items_num = len(self.__history_forward) - \ self.__history_forward.index(path) for _item in range(_items_num): _record_path = self.__history_forward.pop() _first_menuitem = self.__forward_menu.get_children()[0] self.__forward_menu.remove(_first_menuitem) self.__history_back.append(_record_path) if len(self.__history_back) > 1: _menuitem = self._menuItemFactory( self.__history_back[-2]) _menuitem.connect_object("activate", self._menuHistoryBack, self.__history_back[-2], _menuitem) self.__back_menu.prepend(_menuitem) else: # the new active record not is in history forward list # then append back history and clear forward history self.__history_forward[:] = [] for _child in self.__forward_menu.get_children(): self.__forward_menu.remove(_child) self.__history_back.append(path) if len(self.__history_back) > 1: _menuitem = self._menuItemFactory(self.__history_back[-2]) _menuitem.connect_object("activate", self._menuHistoryBack, self.__history_back[-2], _menuitem) self.__back_menu.prepend(_menuitem) while len(self.__history_back) > 100: # list too long self.__history_back.pop(0) _last_menuitem = self.__back_menu.get_children()[-1] self.__back_menu.remove(_last_menuitem) self.__mainWindow.changeHistorySignal() def _getImage(self, record): """_getImage(record) record: record object Returns an image depending on the type of record """ _hierarchy = record.recordType.hierarchy _type = record.recordType.type _subtype = record.recordType.subtype if _hierarchy == 0: _icon = self.__budget_icon elif _hierarchy == 1: _icon = self.__chapter_icon else: if _type == 0: _icon = self.__unit_icon elif _type == 1: _icon = self.__labourforce_icon elif _type == 2: _icon = self.__machinery_icon else: _icon = self.__material_icon _image = Gtk.Image() _image.set_from_pixbuf(_icon) return _image def _menuFactory(self): """_menuFactory() record: record object Creates menus for history back an history forward tool buttons """ # Back Menu # clear menu for _child in self.__back_menu.get_children(): self.__back_menu.remove(_child) # pupulate menu if len(self.__history_back) > 1: for _record_path in self.__history_back[:-1]: _menuitem = self._menuItemFactory(_record_path) _menuitem.connect("activate", self._menuHistoryBack, _record_path, _menuitem) self.__back_menu.prepend(_menuitem) # Forward Menu # clear menu for _child in self.__forward_menu.get_children(): self.__forward_menu.remove(_child) # pupulate menu if len(self.__history_forward) > 0: for _record_path in self.__history_forward[:]: _menuitem = self._menuItemFactory(_record_path) _menuitem.connect("activate", self._menuHistoryForward, _record_path, _menuitem) self.__forward_menu.prepend(_menuitem) def _menuItemFactory(self, record_path): """_menuItemFactory(record_path): record_path: record path Creates and return a menuItem to go to the record """ _code = self.budget.getCode(record_path) _record = self.budget.getRecord(_code) _summary = _record.summary _text = _code.decode("utf8") + " " + _summary.decode("utf8") if len(_text) > 30: _text = _text[:27] + "..." _icon = self._getImage(_record) _grid = Gtk.Grid() _grid.set_orientation(Gtk.Orientation(0)) # 0 Horizontal _grid.set_column_spacing(6) _label = Gtk.Label(_text) _menuitem = Gtk.MenuItem.new() _grid.add(_icon) _grid.add(_label) _menuitem.add(_grid) _menuitem.show_all() return _menuitem def _menuHistoryBack(self, record_path, menu_item): """_menuHistoryBack(record_path, menu_item) Go to the record selected in History Back menu """ if self.budget.hasPath(record_path): self.propagateMessageFrom("change_active", (-1,), record_path) def _menuHistoryForward(self, record_path, menu_item): """_menuHistoryForward(record_path, menu_item) Go to the record selected in History Forward menu """ if self.budget.hasPath(record_path): self.propagateMessageFrom("change_active", (-1,), record_path) def _getActivePathRecord(self): """_getActivePathRecord() Return the Active Path Record """ return self.__active_path_record def _getPreviousPathRecord(self): """_getPreviousPathRecord() Return the Previous Path Record """ if len(self.__history_back) > 1: return self.__history_back[-2] else: return None def _getPosteriorPathRecord(self): """_getPosteriorPathRecord() Return the Posterior Path Record """ if len(self.__history_forward) > 0: return self.__history_forward[-1] else: return None def _getBackMenu(self): """_getBackMenu() Return the Back Menu """ return self.__back_menu def _getForwardMenu(self): """_getForwardMenu() Return the Forward Menu """ return self.__forward_menu def _getTitle(self): """_getTitle() Return the page title, a Gtk.Label objetc """ return self.__title def _getWidget(self): """_getWidget() Return de main widget to show in the pane """ return self.__widget def _setBudget(self, budget): """_setBudget(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.widget _main_widget.show() self.__widget.add(_main_widget) def _getBudget(self): """_getBudget() Return de budget, a "base.Budget" object. """ return self.__budget def _getPanesList(self): """_getPanesList() 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") activePathRecord = property(_getActivePathRecord, None, None, "Active Path Record") previousPathRecord = property(_getPreviousPathRecord, None, None, "Previous Active Path Record") posteriorPathRecord = property(_getPosteriorPathRecord, None, None, "Posterior Active Path Record") back_menu = property(_getBackMenu, None, None, "Back Menu") forward_menu = property(_getForwardMenu, None, None, "Forward Menu") class View(object): """gui.View: Description: It creates a view to show the budget info Constructor: View(view_type, budget, wr_page, pane_path, active_path_record, connected) 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 pane_path: the position or path of the view in the page notebook active_path_record: the record path that must be showed connected: boolean value, True means that the View object sends and receives signals from/to others views. Ancestry: +-- object +-- View Atributes: pane_path: Read-Write. The tuple that identifies the view in the main notebook page widget: Read. the main Gtk widget to show in a view object, a Gtk.Grid object Methods: getItem propagateMessgeFrom runMessage getClone clear """ def __init__(self, view_type, budget, wr_page, pane_path, active_path_record, connected=True): """__init__(view_type, budget, wr_page, pane_path, active_path_record, connected ) 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 pane_path: the position or path of the view in the page notebook active_path_record: the record path that must be showed self.__view_type: the object type to show * DecompositionList * Description * Measure * Sheet of conditions * FileView * CompanyView self.__wr_page: weak reference to the page where the view must be showed self.__budget: the budget to show self.__pane_path: the position or path of the view in the page notebook self.__connected: boolean value, True means that the View object sends and receives signals from/to others views self.__widget: main widget. a Gtk.Grid self.__view: the object to show: * DecompositionList object * Description object * Measure object * Sheet object * FileView object * Comapany View self.__connected_button: a button to switch self.__connected True or False Creates and shows a new view """ self.__view_type = view_type self.__wr_page = wr_page self.__budget = budget self.__pane_path = pane_path self.__connected = connected # 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.new_with_model(_liststore) _cell = Gtk.CellRendererText() _combobox.pack_start(_cell, True) _combobox.add_attribute(_cell, 'text', 0) self.__widget = Gtk.Grid() self.__widget.set_orientation(Gtk.Orientation(1)) # 1 Vertical _hbox = Gtk.Grid() _hbox.set_orientation(Gtk.Orientation(0)) # 0 Horizontal if view_type == "DecompositionList": self.__view = DecompositionList(budget, weakref.ref(self), pane_path, active_path_record) _combobox.set_active(0) _view_icon = Gtk.Image() _view_icon.set_from_file(globalVars.getAppPath( "DECOMPOSITION-ICON")) elif view_type == "RecordDescription": self.__view = Description(budget, weakref.ref(self), pane_path, active_path_record) _combobox.set_active(1) _view_icon = Gtk.Image() _view_icon.set_from_file(globalVars.getAppPath( "DESCRIPTION-ICON")) elif view_type == "Measure": self.__view = Measure(budget, weakref.ref(self), pane_path, active_path_record) _combobox.set_active(2) _view_icon = Gtk.Image() _view_icon.set_from_file(globalVars.getAppPath("MEASURE-ICON")) elif view_type == "Sheet of Conditions": self.__view = Sheet(budget, weakref.ref(self), pane_path, active_path_record) _combobox.set_active(3) _view_icon = Gtk.Image() _view_icon.set_from_file(globalVars.getAppPath("SHEET-ICON")) elif view_type == "FileView": self.__view = FileView(budget, weakref.ref(self), pane_path, active_path_record) _combobox.set_active(4) _view_icon = Gtk.Image() _view_icon.set_from_file(globalVars.getAppPath("FILEVIEW-ICON")) elif view_type == "CompanyView": self.__view = CompanyView(budget, weakref.ref(self), pane_path, active_path_record) _combobox.set_active(5) _view_icon = Gtk.Image() _view_icon.set_from_file(globalVars.getAppPath("COMPANY-ICON")) else: raise ValueError( _(utils.mapping("Invalid type of View: $1", view_type)) ) _view_icon.show() _combobox.connect("changed", self._change_combo) _combobox.show() self.__widget.add(_hbox) self.__widget.add(self.__view.widget) _hbox.add(_view_icon) _hbox.add(_combobox) _invisible = Gtk.Grid() _invisible.set_orientation(Gtk.Orientation(0)) # 0 Horizontal _invisible.set_property("hexpand", True) # expand horizontal space _invisible.show() _hbox.add(_invisible) # TODO : Set thist with config add_menu = False if add_menu: _icon_menu = Gtk.Image() _icon_menu.set_from_file(globalVars.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() _hbox.add(_menu_button) _icon_horizontal = Gtk.Image() _icon_horizontal.set_from_file(globalVars.getAppPath("HORIZONTAL")) _icon_horizontal.show() _horizontal_button = Gtk.ToolButton() _horizontal_button.set_icon_widget(_icon_horizontal) _horizontal_button.connect("clicked", self._split_view, "h") _horizontal_button.set_tooltip_text(_("Split View Left/Right")) _horizontal_button.show() _hbox.add(_horizontal_button) _icon_vertical = Gtk.Image() _icon_vertical.set_from_file(globalVars.getAppPath("VERTICAL")) _icon_vertical.show() _vertical_button = Gtk.ToolButton() _vertical_button.set_icon_widget(_icon_vertical) _vertical_button.connect("clicked", self._split_view, "v") _vertical_button.set_tooltip_text(_("Split View Top/Bottom")) _vertical_button.show() _hbox.add(_vertical_button) self.__connected_button = Gtk.ToolButton() _icon_connected = Gtk.Image() if self.__connected: _icon_path = globalVars.getAppPath("CONNECTED-ICON") _icon_connected.set_from_file(_icon_path) self.__connected_button.set_tooltip_text(_("Disconnect view")) else: _icon_path = globalVars.getAppPath("DISCONNECTED-ICON") _icon_connected.set_from_file(_icon_path) self.__connected_button.set_tooltip_text(_("Connect view")) _icon_connected.show() self.__connected_button.set_icon_widget(_icon_connected) self.__connected_button.connect("clicked", self._connected) self.__connected_button.show() _hbox.add(self.__connected_button) _icon_close = Gtk.Image() _icon_close.set_from_file(globalVars.getAppPath("CLOSE-ICON")) _icon_close.show() _close_button = Gtk.ToolButton() _close_button.set_icon_widget(_icon_close) _close_button.connect("clicked", self._closeItem_from_button) _close_button.set_tooltip_text(_("Close view")) _close_button.show() _hbox.add(_close_button) _hbox.show() self.__widget.show() def getItem(self, pane_path): """getItem(pane_path) Return itself. """ return self def _closeItem_from_menu(self, close_menuitem): """_closeItem_from_menu(close_menuitem) 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.__pane_path) def _closeItem_from_button(self, close_button): """_closeItem_from_button(close_button) 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.__pane_path) def _change_combo(self, combobox): """_change_combo(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.wr_page _pane_path = self.__view.pane_path _path_record = self.__view.active_path_record _hbox = self.__widget.get_children()[1] _view_icon = _hbox.get_child_at(0, 0) _hbox.remove(_view_icon) _view_icon.destroy() self.__widget.remove(self.__view.widget) _view_icon = Gtk.Image() if _index == 0: self.__view = DecompositionList(_budget, _wr_page, _pane_path, _path_record) _view_icon.set_from_file(globalVars.getAppPath( "DECOMPOSITION-ICON")) self.__view_type = "DecompositionList" elif _index == 1: self.__view = Description(_budget, _wr_page, _pane_path, _path_record) _view_icon.set_from_file(globalVars.getAppPath("DESCRIPTION-ICON")) self.__view_type = "RecordDescription" elif _index == 2: self.__view = Measure(_budget, _wr_page, _pane_path, _path_record) _view_icon.set_from_file(globalVars.getAppPath("MEASURE-ICON")) self.__view_type = "Measure" elif _index == 3: self.__view = Sheet(_budget, _wr_page, _pane_path, _path_record) _view_icon.set_from_file(globalVars.getAppPath("SHEET-ICON")) self.__view_type = "Sheet of Conditions" elif _index == 4: self.__view = FileView(_budget, _wr_page, _pane_path, _path_record) _view_icon.set_from_file(globalVars.getAppPath("FILEVIEW-ICON")) self.__view_type = "FileView" elif _index == 5: self.__view = CompanyView(_budget, _wr_page, _pane_path, _path_record) _view_icon.set_from_file(globalVars.getAppPath("COMPANY-ICON")) self.__view_type = "CompanyView" _view_icon.show() _hbox.attach(_view_icon, 0, 0, 1, 1) self.__widget.add(self.__view.widget) def _menu_view(self, widget): """_menu_view(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("activate", self._split_view, "h") _item_leftright.show() _item_topbottom = Gtk.MenuItem(_("Split view Top/Bottom")) _menu_view.append(_item_topbottom) _item_topbottom.connect("activate", self._split_view, "v") _item_topbottom.show() _item_close = Gtk.MenuItem(_("Close view")) _menu_view.append(_item_close) _item_close.connect("activate",self._closeItem_from_menu) _item_close.show() _menu_view.popup_at_widget(widget, Gdk.Gravity(7), Gdk.Gravity(1), Gtk.get_current_event()) def _split_view(self, widget, orientation): """_menu_view(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.__pane_path) def _connected(self, widget): """_connected(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(globalVars.getAppPath("DISCONNECTED-ICON")) _icon.show() self.__connected_button.set_icon_widget(_icon) self.__connected_button.set_tooltip_text(_("Connect View")) self.__connected = False else: _icon = Gtk.Image() _icon.set_from_file(globalVars.getAppPath("CONNECTED-ICON")) _icon.show() self.__connected_button.set_tooltip_text(_("Disconnect View")) self.__connected_button.set_icon_widget(_icon) self.__connected = True def propagateMessageFrom(self, message, pane_path, arg=None): """propagateMessageFrom(message, pane_path, arg=None) message: string message pane_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, pane_path, arg) def runMessage(self, message, pane_path, arg=None): """runMessage(message, pane_path, arg=None) message: the message type "change_active": change the active record "clear": clear instance pane_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, pane_path, arg) def _getWidget(self): """_getWidget() Return de pane widget """ return self.__widget def _getPanePath(self): """_getPanePath() return the tuple that identifies the pane in the notebook page """ return self.__view.pane_path def _setPanePath(self, pane_path): """_setPanePath() set the tuple that identifies the pane in the notebook page """ self.__pane_path = pane_path self.__view.pane_path = pane_path def getClone(self, new_pane_path): """getClone(new_pane_path) new_pane_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_pane_path, self.__view.active_path_record, self.__connected) def clear(self): """clear() Clear the intance atributes """ del self.__wr_page del self.__budget del self.__pane_path del self.__widget del self.__view del self.__connected del self.__connected_button pane_path = property(_getPanePath, _setPanePath, 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.Paned 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 Ancestry: +-- object +-- Paned Atributes: widget: Read. Pane widget("Gtk.Paned" object) pane_path: Read-Write. The paned path in the page Methods: getClone getItem setItem runMessage clear(self) """ # TODO: *control the position paned separator. Now is always in the middle # 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 def __init__(self, orientation, pane_path, item1, item2): """__init__(oritentation, pane_path, item1, item2) orientation: The orientation of de Gtk.Paned, can be "v" or "h" pane_path: the paned path in the page item1: the top or left pane object item2: the bottom or right pane object self.__orientation: The orientation of de Gtk.Paned, can be "v" or "h" self.__widget: Main widget, a Gtk.Paned self.__items: list of items showed in the paned, its can be View or Paned instances self.__pane_path: the paned path in the page 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.Paned.new(Gtk.Orientation(1)) self.__widget.set_wide_handle(True) elif orientation == "h": self.__widget = Gtk.Paned.new(Gtk.Orientation(0)) self.__widget.set_wide_handle(True) else: raise ValueError( _("Invalid orientation.") ) self.__widget.pack1(item1.widget,True,False) self.__widget.pack2(item2.widget,True,False) self.__widget.show() self.__items = [item1, item2] self.__pane_path = pane_path def __getitem__(self, item): """__getitem__(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_pane_path): """getClone(new_pane_path) Return a clone Paned instance with the path new_path """ return Paned(self.__orientation, new_pane_path, self.__items[0].getClone(new_pane_path + (0,)), self.__items[1].getClone(new_pane_path + (1,))) def getItem(self,pane_path): """getItem(pane_path) Return the item whith the specified path. """ _item = self.__items[pane_path[0]] if len(pane_path) == 1: return _item else: return _item.getItem(pane_path[1:]) def setItem(self, pane_path, item_list): """setItem(pane_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 pane_path == 0 or pane_path == 1: _old_item = self.__items[pane_path] self.__widget.remove(_old_item.widget) self.__items[pane_path] = item if pane_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, pane_path, arg=None): """runMessage(message, pane_path, arg=None) message: the message type "change_active": change the active record "clear": clear instance pane_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.pane_path == pane_path: _item.runMessage(message, pane_path, arg) def _getWidget(self): """_getWidget() Return de Gtk.Paned widget """ return self.__widget def _getPanePath(self): """_getPanePath() Return de Paned path in the notebook page """ return self.__pane_path def _setPanePath(self, pane_path): """_setPanePath() sets the tuple that identifies the pane in the notebook page """ self.__pane_path = pane_path self.__items[0].pane_path = pane_path + (0,) self.__items[1].pane_path = pane_path + (1,) def clear(self): """clear() Delete atributes """ del self.__widget del self.__orientation del self.__items del self.__pane_path widget = property(_getWidget, None, None, "Gtk.Paned widget") pane_path = property(_getPanePath, _setPanePath, 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 objects) Methods: createColumn createTextBaseColumn createBaseColumn """ def __init__(self, args): """__init__(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(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', globalVars.color["TEXT"]) _pixbuf_index_cell = Gtk.CellRendererPixbuf() _arrow_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.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, [globalVars.color["INDEX-UNEVEN"], globalVars.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_cells()[0].set_property('xalign', 1.0) return _column elif args[0] == "CALCULATED": _column, cell = self.createTextBaseColumn(args) _column.get_cells()[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', 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('background', globalVars.color["UNEVEN"]) _end_column.pack_start(_end_cell, True) return _end_column return None def createTextBaseColumn(self, args): """createTextBaseColumn(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', 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(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.TreeViewColumnSizing(2)) # 2 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, pane_path, path_record=None) budget: budget showed ("base.Budget" object) page: weak reference from Page instance which creates this class pane_path: tuple that represents the view path in the Page path_record: the record path that must be showed Returns the newly created DecompositionList instance Ancestry: +-- object +-- TreeView +-- DecompositionList Atributes: budget: Read. Budget to show, base.obra object. widget: Read. Window that contains the table, Gtk.ScrolledWindow pane_path: Read-Write. Pane page identifier wr_page: Read-Write. weak ref from Page object which creates this class active_path_record: Read. Active path record Methods: runMessage """ def __init__(self, budget, wr_page, pane_path, path_record=None): """__init__(budget, page, pane_path, path_record=None) budget: budget showed ("base.Budget" object) page: weak reference from Page instance which creates this class pane_path: tuple that represents the path of the List in the Page path_record: the record path that must be showed self.__budget: budget showed ("base.Budget" object) self.__wr_page: weak reference from Page instance which creates self self.__pane_path: tuple that represents the path of the List in the Page self.__liststore: list model which store the list data (Gtk.ListStore object) self.__active_path_record: the record path that must be showed self.__treeview: widget for displaying decomposition lists (Gtk.TreeView) self.__scrolled_window: widget to contain the treeview object self.__chapter_background_colors: background colors of the Code column cells when there is a chapter record, list of color [even cell, uneven cell] self.__chapter_background_colors self.__index_column: Index column (Gtk.TreeViewColumn object) self.__code_column: Record code column (Gtk.TreeViewColumn) self.__type_column: Record Type column (Gtk.TreeViewColumn) self.__unit_column: Unit of measure column (Gtk.TreeViewColumn) self.__description_column: record's short description column (Gtk.TreeViewColumn) self.__measure_column: Measure column (Gtk.TreeViewColumn) self.__price_column: Price column (Gtk.TreeViewColumn) self.__amount_column: Amount column(Gtk.TreeViewColumn) self.__end_column: End empty column (Gtk.TreeViewColumn) self.__chapter_icon: a GdkPixbuf.Pixbuf self.__unit_icon: a GdkPixbuf.Pixbuf self.__material_icon: a GdkPixbuf.Pixbuf self.__machinery_icon: a GdkPixbuf.Pixbuf self.__labourforce_icon: a GdkPixbuf.Pixbuf self.__treeselection: active selection self.__selection_control: state of the selection control (True/False) self.__cursor: cursor position in the table 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.__wr_page = wr_page self.__pane_path = pane_path # ListStore self.__liststore = Gtk.ListStore(object #, int, int, str, str, str, str, str,str ) if path_record is 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() # widget expand all space self.__scrolled_window.set_property("expand", True) self.__scrolled_window.set_policy(Gtk.PolicyType(1), Gtk.PolicyType(1)) # 1 Automatic self.__scrolled_window.add(self.__treeview) # colors _text_color = globalVars.color["TEXT"] _background_color = [ globalVars.color["UNEVEN"], globalVars.color["EVEN"]] self.__chapter_background_colors = [ globalVars.color["CHAPTER-UNEVEN"], globalVars.color["CHAPTER-EVEN"]] super(DecompositionList,self).__init__( [("INDEX",self._selectAll,42), ("CALCULATEDTEXT", self._showParentRecord, 100, _text_color, _background_color), ("PIXBUF", self._showParentRecord, 26, _text_color, _background_color), ("CALCULATEDTEXT", self._showParentRecord, 52, _text_color, _background_color), ("CALCULATEDTEXT", self._showParentRecord, 245, _text_color, _background_color), ("CALCULATED", self._showParentRecord, 90, _text_color, _background_color), ("CALCULATED", self._showParentRecord, 90, _text_color, _background_color), ("CALCULATED", self._showParentRecord, 90, globalVars.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 = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("CHAPTER-ICON")) self.__unit_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("UNIT-ICON") ) self.__material_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("MATERIAL-ICON") ) self.__machinery_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("MACHINERY-ICON")) self.__labourforce_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("LABOURFORCE-ICON")) self.__type_column.get_cells()[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.SelectionMode(3)) # 3 MULTIPLE self.__treeselection.set_select_function(self._controlSelection) self.__selection_control = True if len(self.__liststore) > 0: _tree_path = Gtk.TreePath.new_from_indices((0,)) self.__treeview.set_cursor_on_cell(_tree_path ,self.__unit_column, self.__unit_column.get_cells()[0],True) self.__treeview.grab_focus() self.__cursor = self.__treeview.get_cursor() # Show self._setColumnsHeaders() self.__scrolled_window.show() def _treeviewCursorChanged(self, treeview): """_treeviewCursorChanged(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 != Gdk.EventType(7): # 7 BUTTON_RELEASE if not _column is self.__index_column: self.__cursor = treeview.get_cursor() def _treeviewClickedEvent(self, widget, event): """_treeviewClickedEvent(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): """_treeviewKeyPressEvent(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 in [Gdk.keyval_from_name("Right"), Gdk.keyval_from_name("KP_Right")] \ and _column == self.columns[-2]) \ or (event.keyval in [Gdk.keyval_from_name("Left"), Gdk.keyval_from_name("KP_Left")] \ and _column == self.columns[1]): return True return False def _moveCursor(self, treeview, step, count): """_moveCursor(treeview, step, count) 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, model, path, path_currently_selected, *data): """_controlSelection(selection, model, path, path_currently_selected, *data) 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): """_selectAll(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 if isinstance(self.__cursor[0],Gtk.TreePath): 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): """_setColumnsHeaders() 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, _path_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) _str = _("Code") + chr(10) + "[" + _code.decode("utf8") + "]" self.__code_column.set_title(_str) _str = _("Unit") + chr(10) + "[" + _unit.decode("utf8") + "]" self.__unit_column.set_title(_str) _str = _("Description") + chr(10) + "[" + \ _description.decode("utf8") + "]" self.__description_column.set_title(_str) 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): """_setListstoreValues(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): """_colorCell(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_renderer, tree_model, iter, lcolor) -def celldatamethod(self,column,cell_renderer,tree_model, iter, lcolor) 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) _global_row_path = self.__active_path_record + (_row_path[0],) _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_cells()[1].set_property( 'cell-background', 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, _global_row_path) 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', globalVars.color["ACTIVE"]) else: cell_renderer.set_property('cell-background', lcolor[_number % 2]) def _showParentRecord(self, column): """_showParentRecord(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 if isinstance(self.__cursor[0],Gtk.TreePath): 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.__wr_page() _page.propagateMessageFrom("change_active", self.__pane_path, arg) self.__treeview.set_cursor(_parent, self.__cursor[1]) self.__cursor = self.__treeview.get_cursor() def _showMessageRecord(self, record_path): """_showMessageRecord(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): """_showRowRecord(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 _path_record = self.__active_path_record for _indice in treeview_path.get_indices(): _path_record = _path_record + (_indice,) 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.__wr_page() _page.propagateMessageFrom("change_active", self.__pane_path, _arg ) def runMessage(self, message, pane_path, arg=None): """runMessage(message, pane_path, arg=None) message: the message type "change_active": change the active record "clear": clear instance pane_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): """_clear() it deletes the __budget reference """ del self.__budget def _getWidget(self): """_getWidget() return the main widget (Gtk.ScrolledWindow) """ return self.__scrolled_window def _getPanePath(self): """_getPanePath() return the tuple that identifies the pane in the notebook page """ return self.__pane_path def _setPanePath(self, pane_path): """_setPanePath() sets the tuple that identifies the pane in the notebook page """ self.__pane_path = pane_path def _getWrPage(self): """_getPage() return the Page """ return self.__wr_page def _setWrPage(self,wr_page): """_setWrPage() set the wr_Page """ self.__wr_page = wr_page def _getBudget(self): """_getBudget() return the Budget objet """ return self.__budget def _getActivePathRecord(self): """_getActivePathRecord() return the Active Path Record """ return self.__active_path_record widget = property(_getWidget, None, None, "Pane configuration list") pane_path = property(_getPanePath, _setPanePath, None, "path that identifie the item in the page notebook") wr_page = property(_getWrPage, _setWrPage, 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, pane_path, path_record=None) budget: budget showed ("base.Budget" object) page: weak reference from Page instance which creates this class pane_path: tuple that represents the path of the List in the Page path_record: path of the active record in the budget Ancestry: +-- object +-- TreeView +-- DecompositionList Atributes: budget: Read. Budget to show, base.obra instance. widget: Read. Window that contains the table, Gtk.ScrolledWindow pane_path: Read-Write. Pane page identifier wr_page: Read-Write. weak reference from Page instance which creates this class active_path_record: Read. Path of the active record in the budget Methods: runMessage """ def __init__(self, budget, page, pane_path, path_record=None): """__init__(budget, page, pane_path, path_record=None) budget: budget: budget showed ("base.Budget" object) wr_page: weak reference from Page instance which creates this class pane_path: tuple that represents the path of the List in the Page path_record: path of the active record in the budget self.__budget: budget showed ("base.Budget" object) self.__page: weak reference from Page instance which creates this class self.__pane_path: tuple that represents the path of the List in the Page self.__active_path_record: path of the active record in the budget self.__liststore: list model which store the list data (Gtk.ListStore object) self.__treeview: widget to display decomposition lists (Gtk.TreeView) self.__scrolled_window: widget to scroll the treeview Gtk.ScrolledWindow() self.__chapter_background_colors: The background colors of the Code column cells when there is a chapter record as a list of color [even cell, uneven cell] self.__index_column: Index column (Gtk.TreeViewColumn object) self.__linetype_column: Linetype column (Gtk.TreeViewColumn object) self.__comment_column: Comment column (Gtk.TreeViewColumn) self.__unit_column: Unit column (Gtk.TreeViewColumn) self.__length_column: Legth column (Gtk.TreeViewColumn) self.__width_column: With column (Gtk.TreeViewColumn) self.__height_column: Height column (Gtk.TreeViewColumn) self.__formula_column: Formula column (Gtk.TreeViewColumn) self.__parcial_column: Parcial column (Gtk.TreeViewColumn) self.__subtotal_column: Subtotal column (Gtk.TreeViewColumn) self.__end_column: End empty column (Gtk.TreeViewColumn self.__calculatedline_icon: GdkPixbuf.Pixbuf self.__normalline_icon: GdkPixbuf.Pixbuf self.__parcialline_icon: GdkPixbuf.Pixbuf self.__acumulatedline_icon: GdkPixbuf.Pixbuf self.__treeselection: active selection self.__selection_control: state of the selection control (True/False) self.__cursor: Situation of the cursor in the table 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 path_record is None: path_record = (0,) if not isinstance(budget, base.Budget): raise ValueError( _("Argument must be a Budget object") ) self.__budget = budget self.__wr_page = page self.__pane_path = pane_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() # widget expand all space self.__scrolled_window.set_property("expand", True) self.__scrolled_window.set_policy(Gtk.PolicyType(1), Gtk.PolicyType(1)) # 1 Automatic self.__scrolled_window.add(self.__treeview) # colors _text_color = globalVars.color["TEXT"] _calculated_text = globalVars.color["CALCULATED-TEXT"] _background_color = [ globalVars.color["UNEVEN"], globalVars.color["EVEN"]] self.__chapter_background_colors = [ globalVars.color["CHAPTER-UNEVEN"], globalVars.color["CHAPTER-EVEN"]] super(Measure,self).__init__( [("INDEX",self._selectAll,42), ("PIXBUF", self._passMethod, 40, _text_color, _background_color), ("CALCULATEDTEXT", self._passMethod, 128, _text_color, _background_color), ("CALCULATED", self._passMethod, 55, _text_color, _background_color), ("CALCULATED", self._passMethod, 70, _text_color, _background_color), ("CALCULATED", self._passMethod, 70, _text_color, _background_color), ("CALCULATED", self._passMethod, 70, _text_color, _background_color), ("CALCULATEDTEXT", self._passMethod, 120, _text_color, _background_color), ("CALCULATED", self._passMethod, 70, _calculated_text, _background_color), ("CALCULATED", self._passMethod, 70, _calculated_text, _background_color), ]) # Colums 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 = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("CALCULATEDLINE-ICON")) self.__normalline_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("NORMALLINE-ICON") ) self.__parcialline_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("PARCIALLINE-ICON") ) self.__acumulatedline_icon = GdkPixbuf.Pixbuf.new_from_file( globalVars.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.SelectionMode(3)) # 3 MULTIPLE self.__treeselection.set_select_function(self._controlSelection) self.__selection_control = True if len(self.__liststore) > 0: _tree_path = Gtk.TreePath.new_from_indices((1,)) self.__treeview.set_cursor_on_cell(_tree_path, self.columns[1], self.columns[1].get_cells()[0],True) self.__treeview.grab_focus() self.__cursor = self.__treeview.get_cursor() # Show self._setColumnsHeaders() self.__scrolled_window.show() def _treeviewCursorChanged(self, treeview): """_treeviewCursorChanged(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 != Gdk.EventType(7): # 7 BUTTON_RELEASE if not _column is self.__index_column: self.__cursor = treeview.get_cursor() def _treeviewClickedEvent(self, widget, event): """_treeviewClickedEvent(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): """_treeviewKeyPressEvent(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 in [Gdk.keyval_from_name("Right"), Gdk.keyval_from_name("KP_Right")] \ and _column == self.columns[-2]) \ or (event.keyval in [Gdk.keyval_from_name("Left"), Gdk.keyval_from_name("KP_Left")] \ and _column == self.columns[1]): return True return False def _moveCursor(self, treeview, step, count): """moveCursor(treeview, step, count) 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, model, path, path_currently_selected, *data): """_controlSelection(selection, model, path, path_currently_selected, *data) 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): """_selectAll(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 if isinstance(self.__cursor[0],Gtk.TreePath): 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): """_setColumnsHeaders() Sets the headers column values """ _measure = self.__budget.getMeasure(self.__active_path_record) _DS = self.__budget.getDecimals("DS") _total = _measure.measure _total_str = ("%." + str(abs(_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 _setListstoreValues(self, path_record): """_setListstoreValues(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 ] _treeiter = self.__liststore.append(_values) else: raise ValueError( utils.mapping(_("measure must be a Measure "\ "object. Type: $1"), (str(type(_measure)),)) ) def _colorCell(self, column, cell_renderer, tree_model, iter, lcolor): """_colorCell(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 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_cells()[1].set_property( 'cell-background', 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(abs(_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(abs(_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(abs(_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(abs(_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(abs(_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 = globalVars.color["SUBTOTAL-PARCIAL"] _subtotal = _measure_line.parcial_subtotal else: #elif _type == 2: _color = globalVars.color["SUBTOTAL"] _subtotal = _measure_line.acumulated_subtotal lcolor = [_color, _color] if isinstance(_subtotal, float): _DS = self.__budget.getDecimals("DS") _subtotal= ("%." + str(abs(_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', globalVars.color["ACTIVE"]) else: cell_renderer.set_property('cell-background', lcolor[_number % 2]) def _passMethod(self, column): """_passMethod(column) column: the column that is clicked Method connected to "clicked" event of many columns Do nothing """ pass def _showMessageRecord(self, record_path): """_showMessageRecord(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 runMessage(self, message, pane_path, arg=None): """runMessage(message, pane_path, arg=None) message: the message type "change_active": change the active record "clear": clear instance pane_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): """_clear() it deletes the __budget value """ del self.__budget def _getWidget(self): """_getWidget() return the main widget (Gtk.ScrolledWindow) """ return self.__scrolled_window def _getPanePath(self): """_getPanePath() return the tuple that identifies the pane in the notebook page """ return self.__pane_path def _setPanePath(self, pane_path): """_setPanePath() sets the tuple that identifies the pane in the notebook page """ self.__pane_path = pane_path def _getWrPage(self): """_getPage() return the Page """ return self.__wr_page def _setWrPage(self,wr_page): """_setPage() set the Page """ self.__wr_page = wr_page def _getBudget(self): """_getBudget() return the Budget objet """ return self.__budget def _getActivePathRecord(self): """getActivePathRecord() return the Active Path Record """ return self.__active_path_record widget = property(_getWidget, None, None, "Pane configuration list") pane_path = property(_getPanePath, _setPanePath, None, "Path that identifies the item in the page notebook") wr_page = property(_getWrPage, _setWrPage, 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: budget: budget showed ("base.Budget" object) page: weak reference from Page instance which creates this class pane_path: tuple that represents the view path in the Page path_record: the record path that must be showed Returns the newly created DecompositionList instance Ancestry: +-- object +-- Description Atributes: budget: Read. Budget to show, base.obra object. widget: the main widget (Gtk.ScrolledWindow object) pane_path: Read-Write. Pane page identifier wr_page: Read-Write. weak ref from Page object which creates this class active_path_record: Read. Active path record Methods: runMessage """ # TODO: make standard: "DecompositonList and Description" def __init__(self, budget, wr_page, pane_path, path_record=None): """__init__(budget, page, pane_path, path_record=None) budget: the budget (base.obra object) wr_page: weak reference from Page instance which creates this class pane_path: the path position of the description in the page path_record: the path of the active record self.__budget: the budget (base.obra object) self.__wr_page: weak reference from Page instance which creates self self.__pane_path: the path position of the description in the page self.__active_path_recordthe path of the active record self.__textbuffer: The textbuffer of the textview that contain the record text. self.__label: The Gtk.label with the title of the pane self.__widget: the main pane widget, a Gtk.ScrolledWindow() Creates an shows the scroledwindow that contain the description text of the record to be showed in a pane. """ if path_record is None: path_record = (0,) self.__budget = budget self.__wr_page = wr_page self.__pane_path = pane_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_property("expand", True) # widget expand all space _scrollwindow.set_policy(Gtk.PolicyType(1), Gtk.PolicyType(1)) # 1 Automatic _scrollwindow.set_shadow_type(1) # IN 1 _textview = Gtk.TextView() _textview.set_wrap_mode(2) # 2 Word _textview.set_hexpand(True) _textview.set_vexpand(True) self.__textbuffer = _textview.get_buffer() self.__textbuffer.set_text(_text) _textview.show() _vbox = Gtk.Grid() _vbox.set_orientation(Gtk.Orientation(1)) # 1 Vertical self.__label = Gtk.Label(utils.mapping(_("Description text of the "\ "record $1"), (str(self.__budget.getCode( self.__active_path_record)),))) self.__label.set_alignment(0, 0) self.__label.show() _vbox.add(self.__label) _vbox.add(_textview) _vbox.show() _scrollwindow.add(_vbox) _scrollwindow.show() self.__widget = _scrollwindow def _setActivePathRecord(self, path_record): """_setActivePathRecord(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.decode("utf8"),))) _text = _budget.getRecord(_code).text self.__textbuffer.set_text(_text) def runMessage(self, message, pane_path, arg=None): """runMessage(message, pane_path, arg=None) message: the message type "change_active": change the active record "clear": clear instance pane_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): """_clear() Delete all instance atributes """ del self.__widget del self.__pane_path del self.__budget del self.__active_code del self.__textbuffer del self.__label def _getWidget(self): """_getWidget() return the main widget (Gtk.ScrolledWindow) """ return self.__widget def _getPanePath(self): """_getPanePath() return the tuple that identifies the pane in the notebook page """ return self.__pane_path def _setPanePath(self, pane_path): """_setPanePath() sets the tuple that identifies the pane in the notebook page """ self.__pane_path = pane_path def _getWrPage(self): """_getWrPage() return the weak reference from Page instance """ return self.__wr_page def _setWrPage(self, wr_page): """_setWrPage() set the weak reference from Page instance """ self.__wr_page = wr_page def _getBudget(self): """_getBudget() return the budget object """ return self.__budget def _getActivePathRecord(self): """_getActivePathRecord() return the Active Path Record """ return self.__active_path_record pane_path = property(_getPanePath, _setPanePath, None, "Path that identifie the item in the page notebook") widget = property(_getWidget, None, None, "The main widget (Gtk.ScrolledWindow)") wr_page = property(_getWrPage, _setWrPage, 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: budget: The budget (base.obra objetc) widget: the main widget (Gtk.Grid object) pane_path: the tuple that identifies the pane in the notebook page wr_page: weak reference from Page instance which creates this class active_path_record: The active path record Methods: runMessage """ def __init__(self, budget, wr_page, pane_path, path_record=None): """__init__(budget, wr_page, pane_path, path_record=None) budget: the budget (base.obra object) wr_page: weak reference from Page instance which creates this class pane_path: the path position of the description in the page path_record: the path of the active record self.__budget: the budget (base.obra object) self.__wr_page: weak reference from Page instance which creates self self.__pane_path: the path position of the description in the page self.__active_path_record: the path of the active record self.__label: The Gtk.label with the title of the pane self.__field_liststore: the field liststore self.__field_treeview: the field treeview self.__field_selection: the field selected in field treview self.__section_liststore: the section liststore self.__section_treeview: the section treeview self.__section_selection: the section selected in the section treeview self.__textbuffer: The textbuffer of the textview that contain the record text. self.__widget: main widget, a Gtk.Grid() Creates an shows the scroledwindow that contain the description text of the record to be showed in a pane. """ if path_record is None: path_record = (0,) self.__budget = budget self.__wr_page = wr_page self.__pane_path = pane_path self.__active_path_record = path_record _budget = budget _main_box = Gtk.Grid() _main_box.set_orientation(Gtk.Orientation(1)) # 1 Vertical self.__label = Gtk.Label(utils.mapping(_("Sheet of Conditions of the "\ "record $1"), (self.__budget.getCode( self.__active_path_record),))) self.__label.set_xalign(0) self.__label.set_yalign(0) self.__label.show() _frame = Gtk.Frame() _frame.set_shadow_type(Gtk.ShadowType(1)) # 1 In _frame_box = Gtk.Grid() _frame_box.set_orientation(Gtk.Orientation(1)) # 1 Vertical _list_box = Gtk.Grid() _list_box.set_orientation(Gtk.Orientation(0)) # 0 Horizontal 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.SelectionMode(1)) # 1 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.PolicyType(1), Gtk.PolicyType(1)) # 1 Automatic # widget expand all space _field_scrollwindow.set_property("hexpand", True) _field_scrollwindow.add(self.__field_treeview) _field_scrollwindow.set_size_request(-1, 80) _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.SelectionMode(1)) # 1 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.PolicyType(1), Gtk.PolicyType(1)) # 1 Automatic # widget expand all space _section_scrollwindow.set_property("hexpand", True) _section_scrollwindow.set_size_request(-1, 90) _section_scrollwindow.add(self.__section_treeview) _section_scrollwindow.show() _list_box.add(_field_scrollwindow) _list_box.add(_section_scrollwindow) _list_box.show() _scrollwindow = Gtk.ScrolledWindow() _scrollwindow.set_policy(Gtk.PolicyType(1), Gtk.PolicyType(1)) # 1 Automatic _scrollwindow.set_property("expand", True) # widget expand all space _textview = Gtk.TextView() _textview.set_wrap_mode(2) # 2 Word _textview.set_property("expand", True) # widget expand all space self.__textbuffer = _textview.get_buffer() _textview.show() _hbox = Gtk.Grid() _hbox.set_orientation(Gtk.Orientation(0)) # 0 Horizontal _hbox.add(_textview) _hbox.show() _frame_box.add(self.__label) _frame_box.add(_list_box) _frame_box.show() _frame.add(_frame_box) _frame.show() _main_box.add(_frame) _vbox = Gtk.Grid() _vbox.set_orientation(Gtk.Orientation(1)) # 1 Vertical _vbox.add(_hbox) _vbox.show() _main_box.add(_scrollwindow) _main_box.show() _scrollwindow.add(_vbox) _scrollwindow.show() self.__widget = _main_box self._setFields() def _setFields(self): """_setFields() 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() 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() 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, model, path, path_currently_selected, *data): """_controlSelection(selection, model, path, path_currently_selected, *data) selection: treeselection path: selected path 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(path) self.__field_selection = self.__field_liststore.get_value(_treeiter, 0) self._setSection() return True def _section_controlSelection(self, selection, model, path, path_currently_selected, *data): """_section_controlSelection(selection, model, path, path_currently_selected, *data) selection: treeselection path: selected path 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(path) self.__section_selection = self.__section_liststore.get_value(_treeiter, 0) self._setText() return True def _setActivePathRecord(self, path_record): """_setActivePathRecord(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, pane_path, arg=None): """runMessage(message, pane_path, arg=None) message: the message type "change_active": change the active record "clear": clear instance pane_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): """_clear() Deletes all the instance atributes """ del self.__wr_page del self.__widget del self.__pane_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): """_getWidget() return the main widget (Gtk.ScrolledWindow) """ return self.__widget def _getPanePath(self): """_getPanePath() return the tuple that identifies the pane in the notebook page """ return self.__pane_path def _setPanePath(self, pane_path): """_setPanePath() sets the tuple that identifies the pane in the notebook page """ self.__pane_path = pane_path def _getWrPage(self): """_getWrPage() return the weak reference from Page instance """ return self.__wr_page def _setWrPage(self, wr_page): """_setWrPage() set the weak reference from Page instance """ self.__wr_page = wr_page def _getBudget(self): """_getBudget() return the budget object """ return self.__budget def _getActivePathRecord(self): """_getActivePathRecord() return the Active Path Record """ return self.__active_path_record pane_path = property(_getPanePath, _setPanePath, None, "Path that identifie the item in the page notebook") widget = property(_getWidget, None, None, "Lista de configuracion de vistas") wr_page = property(_getWrPage, _setWrPage, 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, page, pane_path, path_record=None) budget: the budget (base.obra object) wr_page: weak reference from Page instance which creates this class pane_path: the path position of the description in the page path_record: the path of the active record Ancestry: +-- object +-- FileView Atributes: widget: the main widget (Gtk.ScrolledWindow object) pane_path: the tuple that identifies the pane in the notebook page budget: The budget (base.obra object) active_path_record: Read. wr_page: Read-Write. weak reference from Page instance Methods: runMessage """ def __init__(self, budget, wr_page, pane_path, path_record=None): """__init__(budget, page, pane_path, path_record=None) budget: the budget (base.obra object) wr_page: weak reference from Page instance which creates this class pane_path: the path position of the description in the page path_record: the path of the active record self.__budget: the budget (base.obra object) self.__wr_page: weak reference from Page instance which creates self self.__pane_path: the path position of the description in the page self.__active_path_record: the path of the active record self.__active_code: the code of the active record self.__icon_box: the box that contains the icon self.__widget: main widget, a Gtk.ScrolledWindow Creates an shows the scroledwindow that contain icon files of the record to be showed in a pane. """ if path_record is None: path_record = (0,) self.__budget = budget self.__wr_page = wr_page self.__pane_path = pane_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.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.__icon_box.show() _scrollwindow.add(self.__icon_box) _scrollwindow.show() self.__widget = _scrollwindow def _getIconBox(self, record): """_getIconBox(record) record: the active record object Creates and returns the box whith te icon files of the active record. """ _files = record.getFiles() _flowbox = Gtk.FlowBox() _flowbox.set_valign(Gtk.Align.START) _flowbox.set_max_children_per_line(30) _flowbox.set_selection_mode(Gtk.SelectionMode.NONE) _flowbox.set_property("expand", True) # widget expand all space for _file in _files: _path = os.path.dirname(self.__budget.filename) _file_path = os.path.join(_path, _file.name) _box = Gtk.Grid() _box.set_orientation(Gtk.Orientation(1)) # 1 Vertical if os.path.exists(_file_path): _filetype = utils.getFiletype(_file_path) _event_box = Gtk.LinkButton() _file_icon = Gtk.Image() # "image", "wmf", "dxf", "pdf" , "video", # "office-document", "office-presentation", # "office-spreadsheet", "html", "rtf", "txt", "" # icon if _filetype in ["image", "wmf"]: try: _image_pixbuf = GdkPixbuf.Pixbuf.new_from_file(_file_path) _image_pixbuf = _image_pixbuf.scale_simple(64, 64, GdkPixbuf.InterpType(2)) # 2 BILINEAR except: _image_pixbuf = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("IMAGE-ICON")) _image_pixbuf = _image_pixbuf.scale_simple(64, 64, GdkPixbuf.InterpType(2)) # 2 BILINEAR _file_icon.set_from_pixbuf(_image_pixbuf) _event_box.connect("activate-link", self._launchFile, _filetype, _file_path) elif _filetype == "dxf": _dxf_pixbuf = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("DXF-ICON")) _dxf_pixbuf = _dxf_pixbuf.scale_simple(64, 64, GdkPixbuf.InterpType(2)) # 2 BILINEAR _file_icon.set_from_pixbuf(_dxf_pixbuf) _event_box.connect("activate-link", self._launchFile, "dxf", _file_path) elif _filetype == "pdf": _pdf_pixbuf = GdkPixbuf.Pixbuf.new_from_file( globalVars.getAppPath("PDF-ICON")) _pdf_pixbuf = _pdf_pixbuf.scale_simple(64, 64, GdkPixbuf.InterpType(2)) # 2 BILINEAR _file_icon.set_from_pixbuf(_pdf_pixbuf) _event_box.connect("activate-link", self._launchFile, "pdf", _file_path) elif _filetype == "video": _video_pixbuf = Gtk.IconTheme.get_default().load_icon( "video-x-generic", 64, 0) _file_icon.set_from_pixbuf(_video_pixbuf) _event_box.connect("activate-link", self._launchFile, "video", _file_path) elif _filetype == "office-document": _document_pixbuf = Gtk.IconTheme.get_default().load_icon( "x-office-document", 64, 0) _file_icon.set_from_pixbuf(_document_pixbuf) _event_box.connect("activate-link", self._launchFile, "office-document", _file_path) elif _filetype == "office-presentation": _presentation_pixbuf = Gtk.IconTheme.get_default().load_icon( "x-office-presentation", 64, 0) _file_icon.set_from_pixbuf(_presentation_pixbuf) _event_box.connect("activate-link", self._launchFile, "office-presentation", _file_path) elif _filetype == "office-spreadsheet": _spreadsheet_pixbuf = Gtk.IconTheme.get_default().load_icon( "x-office-spreadsheet", 64, 0) _file_icon.set_from_pixbuf(_spreadsheet_pixbuf) _event_box.connect("activate-link", self._launchFile, "office-spreadsheet", _file_path) elif _filetype == "html": _html_pixbuf = Gtk.IconTheme.get_default().load_icon( "text-html", 64, 0) _file_icon.set_from_pixbuf(_html_pixbuf) _event_box.connect("activate-link", self._launchFile, "html", _file_path) elif _filetype == "rtf": _rtf_pixbuf = Gtk.IconTheme.get_default().load_icon( "text-x-generic", 64, 0) _file_icon.set_from_pixbuf(_rtf_pixbuf) _event_box.connect("activate-link", self._launchFile, "rtf", _file_path) elif _filetype == "txt": _txt_pixbuf = Gtk.IconTheme.get_default().load_icon( "text-x-generic", 64, 0) _file_icon.set_from_pixbuf(_txt_pixbuf) _event_box.connect("activate-link", self._launchFile, "txt", _file_path) else: _missing_pixbuf = Gtk.IconTheme.get_default().load_icon( "image-missing", 64, 0) _file_icon.set_from_pixbuf(_missing_pixbuf) # Is secure open no detected filetype? #_event_box.connect("activate-link", self._launchFile, # "", _file_path) _event_box = Gtk.EventBox() _event_box.add(_file_icon) _event_box.props.margin = 5 _box.add(_event_box) _file_icon.show() _event_box.show() # label _label_event_box = Gtk.EventBox() _label = Gtk.Label(_file.name) _label_event_box.add(_label) _label_event_box.show() _label.show() _label_event_box.props.margin = 5 _box.add(_label_event_box) _box.show() _box.props.margin = 5 _flowbox.add(_box) _flowbox.show() #_scrolled.show() return _flowbox def _launchFile(self, widget, kind, file_path): """_launchFile(widget, event, kind, file_path) widget: the widget that emit the signal king: kind of file file_path: the path file to be launch Launch the file if a click emit the signal. Method connected to "activate-link" signal in images botton link Return True: stops propagate event and avoids to raise an error when opening an empty uri. """ openwith.launch_file(kind, file_path) return True def _setActivePathRecord(self, path_record): """_setActivePathRecord(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, pane_path, arg=None): """runMessage(message, pane_path, arg=None) message: the message type "change_active": change the active record "clear": clear instance pane_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): """_clear() Delete all instance atributes """ del self.__widget del self.__pane_path del self.__budget del self.__active_code def _getWidget(self): """_getWidget() return the main widget (Gtk.ScrolledWindow) """ return self.__widget def _getPanePath(self): """_getPanePath() return the tuple that identifies the pane in the notebook page """ return self.__pane_path def _setPanePath(self, pane_path): """_setPanePath() sets the tuple that identifies the pane in the notebook page """ self.__pane_path = pane_path def _getWrPage(self): """_getWrPage() return the weak reference from Page instance """ return self.__wr_page def _setWrPage(self, wr_page): """setPage() set the weak reference from Page instance """ self.__wr_page = wr_page def _getBudget(self): """getBudget() return the budget object """ return self.__budget def _getActivePathRecord(self): """_getActivePathRecord() return the Active Path Record """ return self.__active_path_record pane_path = property(_getPanePath, _setPanePath, None, "Path that identifie the item in the page notebook") widget = property(_getWidget, None, None, "The main widget (Gtk.ScrolledWindow)") wr_page = property(_getWrPage, _setWrPage, 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 CompanyView(object): """gui:CompanyView: Description: Class to show the company records of a budget Constructor: CompanyView(budget, wr_page, pane_path, path_record=(None) budget: budget showed ("base.Budget" object) wr_page: weak reference from Page instance which creates this class pane_path: tuple that represents the path of the List in the Page path_record: path of the active record in the budget Ancestry: +-- object +-- CompanyView Atributes: active_path_record: Read. Path of the active record in the budget widget: Read. Window that contains the main widget, a Gtk.Paned pane_path: Read-Write. Pane page identifier wr_page: Read-Write. weak reference from Page instance budget: Read. Budget to show, base.budget instance. Methods: runMessage """ def __init__(self, budget, wr_page, pane_path, path_record=None): """__init__(budget, wr_page, pane_path, path_record=None) budget: budget showed ("base.Budget" object) wr_page: weak reference from Page instance which creates this class pane_path: tuple that represents the path of the pane in the Page path_record: path of the active record in the budget self.__selection: "company" or "office" selected treeview self.__budget: budget: budget showed ("base.Budget" object) self.__wr_page: weak reference from Page instance self.__pane_path: tuple that represents the path of the List in the Page self.__active_path_record: path of the active record in the budget self.__widget: main widget, a Gtk.Paned self.__treestore: to store companys data self.__option_View: OptionView object Creates an shows the widgets with the company data. """ if path_record is None: path_record = (0,) self.__selection = None # Seting init args if not isinstance(budget, base.Budget): raise ValueError( _("Argument must be a Budget object") ) self.__budget = budget self.__wr_page = wr_page self.__pane_path = pane_path self.__active_path_record = path_record # main widget self.__widget = Gtk.Paned.new(Gtk.Orientation(0)) # 0 Horizontal self.__widget.set_position(230) # TreeStore self.__treestore = Gtk.TreeStore(str, str) self._setTreeStoreValues() # Select Treeview _select_treeview = Gtk.TreeView(self.__treestore) _select_treeview.set_enable_search(False) _select_treeview.set_reorderable(False) _select_treeview.set_headers_visible(False) _select_treeview.show() # Scrolled_window _scrolled_window = Gtk.ScrolledWindow() _scrolled_window.set_property("expand", True) # widget expand all space _scrolled_window.set_policy(Gtk.PolicyType(1), Gtk.PolicyType(1)) # 1 Automatic _scrolled_window.add(_select_treeview) # colors _text_color = globalVars.color["TEXT"] _background_color = [ globalVars.color["UNEVEN"], globalVars.color["EVEN"]] _code_column = Gtk.TreeViewColumn() _code_column.set_clickable(True) _code_column.set_fixed_width(200) _code_cell = Gtk.CellRendererText() _code_cell.set_property('foreground', _text_color) _code_column.pack_start(_code_cell, True) _code_column.add_attribute(_code_cell, 'text', 0) _summary_cell = Gtk.CellRendererText() _summary_cell.set_property('foreground', _text_color) _code_column.pack_start(_summary_cell, True) _code_column.add_attribute(_summary_cell, 'text', 1) # Index column _select_treeview.append_column(_code_column) # control selection _treeselection = _select_treeview.get_selection() _treeselection.set_mode(Gtk.SelectionMode(1)) # 1 SINGLE _treeselection.set_select_function(self._controlSelection) # Show _scrolled_window.show() # Option View self.__option_View = OptionView() # Add to main widget self.__widget.add1(_scrolled_window) self.__widget.add2(self.__option_View.widget) # Selection _select_treeview.set_cursor((0,), None, False) _select_treeview.grab_focus() # Show self.__widget.show() def _setOptions(self, type): """_setOptions(type) type: "company" or "office" Sets the Options in the OptionView """ 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.options = _options elif type == "office": _options = [("officeType", _("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.options = _options else: print(_("Unknow Option Type") ) def _setTreeStoreValues(self): """_setTreeStoreValues() 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 _controlSelection(self, selection, model, path, path_currently_selected, *data): """_controlSelection(selection, model, path, path_currently_selected, *data) 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(path) == 1: # The selection is a company _company_key = self.__treestore[path][0] _company = self.__budget.getCompany(_company_key) _selection = "company" _values = _company.values else: # The selection is a office _company_key = self.__treestore[path[:1]][0] _company = self.__budget.getCompany(_company_key) _selection = "office" _office = _company.offices[path[1]] _values = _office.values if not self.__selection == _selection: self.__selection = _selection self._setOptions(_selection) self.__option_View.values = _values return True def _showMessageRecord(self, record_path): """_showMessageRecord(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, pane_path, arg=None): """runMessage(message, pane_path, arg=None) message: the message type "change_active": change the active record "clear": clear instance pane_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): """_colorCell(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_cells()[1].set_property( 'cell-background', lcolor[_number % 2]) if self.__treeview.get_cursor() == (_row_path,column): cell_renderer.set_property('cell-background', globalVars.color["ACTIVE"]) else: cell_renderer.set_property('cell-background', lcolor[_number % 2]) def _clear(self): """_clear() it deletes the self.__budget value """ del self.__budget def _getWidget(self): """_getWidget() return the main widget (Gtk.ScrolledWindow) """ return self.__widget def _getPanePath(self): """_getPanePath() return the tuple that identifies the pane in the notebook page """ return self.__pane_path def _setPanePath(self, pane_path): """_setPanePath() sets the tuple that identifies the pane in the notebook page """ self.__pane_path = pane_path def _getWrPage(self): """_getWrPage() return the Page """ return self.__wr_page def _setWrPage(self,wr_page): """_setWrPage() set the Page """ self.__wr_page = wr_page def _getBudget(self): """_getBudget() return the Budget objet """ return self.__budget def _getActivePathRecord(self): """_getActivePathRecord() 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") pane_path = property(_getPanePath, _setPanePath, None, "Path that identifies the item in the page notebook") wr_page = property(_getWrPage, _setWrPage, 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() Ancestry: +-- object +-- OptionView Atributes: widget: Read. Main widget options: Write values: Write Methods: No public Methods """ def __init__(self): """__init__() self.__option_dict: {"option key" : ["option name", "value", "option type", "option_description"]} self.__option_list: option keys list self.__option_types: valid option types list self.__liststore: Gtk.ListStore self.__treeview: Gtk.TreeView self.__option_column: option column self.__value_column: value column self.__type_column: type column self.__description_label: Gtk.Label self.__widget: Main widget Creates an shows the widget that contain the option data. """ 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.Grid() _vbox.set_orientation(Gtk.Orientation(1)) # 1 Vertical # Scrolled_window _scrolled_window = Gtk.ScrolledWindow() _scrolled_window.set_property("expand", True) # widget expand all space _scrolled_window.set_policy(Gtk.PolicyType(1), Gtk.PolicyType(1)) # 1 Automatic _scrolled_window.add(self.__treeview) _scrolled_window.show() _vbox.add(_scrolled_window) # colors _text_color = globalVars.color["TEXT"] _background_color = [ globalVars.color["UNEVEN"], globalVars.color["EVEN"]] # Option Column self.__option_column = Gtk.TreeViewColumn() self.__option_column.set_sizing(Gtk.TreeViewColumnSizing(2)) # 2 Fixed self.__option_column.set_fixed_width(150) self.__option_column.set_resizable(True) _option_cell = Gtk.CellRendererText() _option_cell.set_property('foreground', _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.TreeViewColumnSizing(2)) # 2 Fixed self.__value_column.set_fixed_width(275) self.__value_column.set_resizable(True) _value_cell = Gtk.CellRendererText() _value_cell.set_property('foreground', _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.TreeViewColumnSizing(2)) # 2 Fixed self.__type_column.set_fixed_width(70) self.__type_column.set_resizable(True) _type_cell = Gtk.CellRendererText() _type_cell.set_property('foreground', _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', globalVars.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 _treeselection = self.__treeview.get_selection() _treeselection.set_mode(Gtk.SelectionMode(3)) # 3 MULTIPLE _treeselection.set_select_function(self._controlSelection) # labels _frame = Gtk.Frame() _frame.set_shadow_type(2) # OUT 2 _vbox2 = Gtk.Grid() _vbox2.set_orientation(Gtk.Orientation(1)) # 1 Vertical _frame.add(_vbox2) _label = Gtk.Label() _label.set_xalign(0) _label.set_yalign(0) _label.set_margin_start(12) _label.set_markup("<b>" + _("Description:") + "</b>") _label.show() self.__description_label = Gtk.Label() self.__description_label.set_xalign(0) self.__description_label.set_yalign(0) self.__description_label.set_margin_start(24) self.__description_label.show() _vbox2.add(_label) _vbox2.add(self.__description_label) _vbox2.show() _frame.show() _vbox.add(_frame) # Show self.__treeview.show() _vbox.show() self.__widget = _vbox def _treeviewKeyPressEvent(self, widget, event): """_treeviewKeyPressEvent(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 press 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 in [Gdk.keyval_from_name("Right"), Gdk.keyval_from_name("KP_Right")] \ and _column == self.__value_column) \ or (event.keyval in [Gdk.keyval_from_name("Left"), Gdk.keyval_from_name("KP_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): """_treeviewClickedEvent(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, model, path, path_currently_selected, *data): """_controlSelection(selection, model, path, path_currently_selected, *data) 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): """_colorCell(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 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', globalVars.color["ACTIVE"]) else: cell_renderer.set_property('cell-background', 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(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] #-# str and unicode if (isinstance(_option_key, str) or \ isinstance(_option_key, unicode)) 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") ) def _setValues(self, values): """_setValues(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): if Gdk.RGBA().parse(_value): 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") ) print(_option, _value) else: print( _("Values must be a dict") ) self.__treeview.set_cursor(Gtk.TreePath.new_from_indices((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): """_getWidget() return the main widget (Gtk.ScrolledWindow) """ return self.__widget widget = property(_getWidget, None, None, "main widget") values = property(None, _setValues, None, "values") options = property(None, _setOptions, None, "options")