From 72748498b2f88ed616552440a78d28133a3c7d49 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Mon, 3 Jun 2013 14:34:44 +0000 Subject: [PATCH] GEP 18 - General improvement: Add a cited in tab to sources for general overview of uses svn: r22472 --- gramps/gen/lib/srctemplate.py | 10 +- gramps/gui/editors/displaytabs/__init__.py | 1 + gramps/gui/editors/displaytabs/citedintab.py | 410 ++++++++++++++++++ gramps/gui/editors/displaytabs/grampstab.py | 4 +- .../gui/editors/displaytabs/srctemplatetab.py | 48 +- gramps/gui/editors/editcitation.py | 10 +- gramps/gui/editors/editsource.py | 27 +- gramps/gui/widgets/srctemplatetreeview.py | 13 +- 8 files changed, 483 insertions(+), 40 deletions(-) create mode 100644 gramps/gui/editors/displaytabs/citedintab.py diff --git a/gramps/gen/lib/srctemplate.py b/gramps/gen/lib/srctemplate.py index 5e392c964..c407a46d4 100644 --- a/gramps/gen/lib/srctemplate.py +++ b/gramps/gen/lib/srctemplate.py @@ -244,6 +244,9 @@ class SrcTemplate(object): """ Compute the reference based on data present. At the moment no style is applied! + + THIS IS UGLY CODE AT THE MOMENT! SHOULD BE ENTIRELY REWRITTEN, FOR + NOW IT JUST GIVES ME SOMETHING TO USE IN THE PROTOTYPE !! """ reflist = self.tempstruct[reftype] # reflist is typically a list like @@ -292,7 +295,7 @@ class SrcTemplate(object): fieldadded[-1] = True ref[-1] += ldeltodo if len(ref[-1]) and ref[-1][-1] == '.': - ref[-1] += ' ' + field.capitalize() + ref[-1] += ' ' + field[0].capitalize() + field[1:] elif len(ref[-1]) and ref[-1][-1] in [',', ':', '-']: ref[-1] += ' ' + field else: @@ -372,7 +375,10 @@ class SrcTemplate(object): fieldadded[-1] = False ref = ''.join(ref) - return ref.capitalize() + if ref: + return ref[0].capitalize() + ref[1:] + else: + return ref def author_gedcom(self, attr_list=None): if attr_list: diff --git a/gramps/gui/editors/displaytabs/__init__.py b/gramps/gui/editors/displaytabs/__init__.py index d81625416..0a6eda2bf 100644 --- a/gramps/gui/editors/displaytabs/__init__.py +++ b/gramps/gui/editors/displaytabs/__init__.py @@ -39,6 +39,7 @@ from .embeddedlist import EmbeddedList, TEXT_COL, MARKUP_COL, ICON_COL from .addrembedlist import AddrEmbedList from .attrembedlist import AttrEmbedList from .backreflist import BackRefList +from .citedintab import CitedInTab from .eventbackreflist import EventBackRefList from .eventembedlist import EventEmbedList from .familyattrembedlist import FamilyAttrEmbedList diff --git a/gramps/gui/editors/displaytabs/citedintab.py b/gramps/gui/editors/displaytabs/citedintab.py new file mode 100644 index 000000000..d82b49e33 --- /dev/null +++ b/gramps/gui/editors/displaytabs/citedintab.py @@ -0,0 +1,410 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2013 Benny Malengier +# +# This program 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 2 of the License, or +# (at your option) any later version. +# +# This program 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +from __future__ import print_function +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +from gramps.gen.const import GRAMPS_LOCALE as glocale +_ = glocale.translation.gettext + +#------------------------------------------------------------------------- +# +# GTK libraries +# +#------------------------------------------------------------------------- +from gi.repository import Gdk +from gi.repository import Gtk + +#------------------------------------------------------------------------- +# +# Gramps libraries +# +#------------------------------------------------------------------------- +from gramps.gen.display.name import displayer as _nd +from gramps.gen.utils.db import (get_citation_referents, family_name, + get_participant_from_event) +from .grampstab import GrampsTab +from ...widgets import SimpleButton + +#------------------------------------------------------------------------- +# +# Classes +# +#------------------------------------------------------------------------- +class CitedInTab(GrampsTab): + """ + This class provides the tabpage for overview of where a source is + cited. + It shows these objects in a treeviewl and allows to load citations in the + top part of the source editor. + """ + def __init__(self, dbstate, uistate, track, src): + """ + @param dbstate: The database state. Contains a reference to + the database, along with other state information. The GrampsTab + uses this to access the database and to pass to and created + child windows (such as edit dialogs). + @type dbstate: DbState + @param uistate: The UI state. Used primarily to pass to any created + subwindows. + @type uistate: DisplayState + @param track: The window tracking mechanism used to manage windows. + This is only used to pass to generted child windows. + @type track: list + @param src: source which we manage in this tab + @type src: gen.lib.Source + @param glade: glade objects with the needed widgets + """ + self.src = src + self.readonly = dbstate.db.readonly + self.srtdata = [] + + GrampsTab.__init__(self, dbstate, uistate, track, _("Cited In")) + self._set_label() + + def build_interface(self): + """ + method called in init of GrampsTab + """ + self.generate_data() + self.build_model() + self.setup_interface() + self.show_all() + + def get_icon_name(self): + return 'gramps-citation' + + def is_empty(self): + """ + Return True if there is no data to show + """ + return len(self.srtdata) == 0 + + def generate_data(self): + """ + Obtain all objects this source is cited in + """ + self.srtdata = [] + self.obj2citemap = {} + if not self.src.handle: + #new object + return + db = self.dbstate.db + #we don't nest calls to find_backlink_handles, so obtain first for + #the source, then in loop for citations + listtopobj = [x for x in db.find_backlink_handles(self.src.handle)] + for (cobjclass, chandle) in listtopobj: + #this will only be citations! + ##print ('t1', cobjclass, chandle) + if cobjclass == 'Citation': + cite = db.get_citation_from_handle(chandle) + for (objclass, handle) in db.find_backlink_handles(chandle): + ##print ('t2', objclass, handle) + if objclass == 'Person': + ref = db.get_person_from_handle(handle) + self.__add_person(ref, cite) + elif objclass == 'Family': + ref = db.get_family_from_handle(handle) + self.__add_family(ref, cite) + elif objclass == 'Event': + ref = db.get_event_from_handle(handle) + self.__add_event(ref, cite) + elif objclass == 'Place': + ref = db.get_place_from_handle(handle) + self.__add_place(ref, cite) + elif objclass == 'Repository': + ref = db.get_repository_from_handle(handle) + self.__add_repo(ref, cite) + elif objclass in ['MediaObject', 'Media']: + ref = db.get_object_from_handle(handle) + self.__add_media(ref, cite) + else: + #most strange, not possible for citation there! + print ("Error in citedintab.py: citation referenced " + "outside citation") + else: + #most strange, not possible ! + print ("Error in citedintab.py: source referenced " + "outside citation") + self.srtdata = sorted(self.srtdata, key=lambda x: glocale.sort_key(x[0])) + + def __add_object(self, obj, cite, descr_obj, shortdescr): + """ + obtain citation data of the object and store here so it can be shown + in a treeview + """ + if not obj.handle in self.obj2citemap: + self.obj2citemap[obj.handle] = {'prim': [], 'sec': [], 'subsec': []} + #add for sorting in the treeview to map + self.srtdata.append((descr_obj, obj.handle, shortdescr)) + #we analyse the object to determine where the citation is used. + if hasattr(obj, 'get_citation_list'): + for citehandle in obj.get_citation_list(): + ##print ('t4', citehandle) + if cite.handle == citehandle: + self.obj2citemap[obj.handle]['prim'].append(cite.handle) + #now search the citation in secondary objects. This can maximally be + # 2 levels deep, eg citation in attribute of eventref + for objsec in obj.get_citation_child_list(): + ##print ('t5', objsec) + if hasattr(objsec, 'get_citation_list'): + for citehandle in objsec.get_citation_list(): + ##print ('t6', citehandle) + if cite.handle == citehandle: + self.obj2citemap[obj.handle]['sec'].append( + (cite.handle, self.format_sec_obj(objsec))) + + if hasattr(objsec, 'get_citation_child_list'): + for objsubsec in objsec.get_citation_child_list(): + ##print ('t7', objsubsec) + #eg attribute of eventref of person + for citehandle in objsubsec.get_citation_list(): + if cite.handle == citehandle: + self.obj2citemap[obj.handle]['subsec'].append( + (cite.handle, + _('%(first)s -> %(sec)s') % { + 'first': self.format_sec_obj(objsec), + 'sec' : self.format_sec_obj(objsubsec)})) + + def __add_person(self, obj, cite): + """ + see __add_object + """ + name = _nd.display_name(obj.get_primary_name()) + self.__add_object(obj, cite, _("Person %(id)s: %(descr)s") % { + 'id': obj.get_gramps_id(), + 'descr': name}, _("Cited in Person")) + + def __add_family(self, obj, cite): + """ + see __add_object + """ + name = family_name(obj, self.dbstate.db, _("Unknown Family")) + self.__add_object(obj, cite, _("Family %(id)s: %(descr)s") % { + 'id': obj.get_gramps_id(), + 'descr': name}, _("Cited in Family")) + + def __add_event(self, obj, cite): + """ + see __add_object + """ + who = get_participant_from_event(self.dbstate.db, obj.handle) + desc = obj.get_description() + event_name = obj.get_type() + if desc: + event_name = '%s - %s' % (event_name, desc) + if who: + event_name = '%s - %s' % (event_name, who) + name = _('Event %(id)s: %(descr)s') % { + 'id': obj.get_gramps_id(), + 'descr': event_name} + self.__add_object(obj, cite, name, _("Cited in Event")) + + def __add_place(self, obj, cite): + """ + see __add_object + """ + self.__add_object(obj, cite, _('Place %(id)s: %(descr)s') % { + 'id': obj.get_gramps_id(), + 'descr': obj.get_title()}, _("Cited in Place")) + + def __add_repo(self, obj, cite): + """ + see __add_object + """ + self.__add_object(obj, cite, _('Repository %(id)s: %(descr)s') % { + 'id': obj.get_gramps_id(), + 'descr': obj.get_name()}, _("Cited in Repository")) + + def __add_media(self, obj, cite): + """ + see __add_object + """ + name = obj.get_description().strip() + if not name: + name = obj.get_path() + if not name: + name = obj.get_mime_type() + self.__add_object(obj, cite, _('Media %(id)s: %(descr)s') % { + 'id': obj.get_gramps_id(), + 'descr': name}, _("Cited in Media")) + + def format_sec_obj(self, objsec): + """ + text for treeview on citation in secondary object + """ + classname = objsec.__class__.__name__ + classobj = classname + descr = '' #TODO TO SET THIS !! + if classname == "Address": + descr = objsec.get_street() + classobj = _("Address") + elif classname == "Attribute": + descr = str(objsec.get_type()) + classobj = _("Attribute") + elif classname == "ChildRef": + ref = objsec.get_reference_handle() + person = self.dbstate.db.get_person_from_handle(ref) + descr = _nd.display_name(person.get_primary_name()) + classobj = _("Child") + elif classname == "EventRef": + ref = objsec.get_reference_handle() + event = self.dbstate.db.get_event_from_handle(ref) + descr = str(event.get_type()) + classobj = _("Event Reference") + elif classname == "LdsOrd": + descr = objsec.type2str() + classobj = _("LDS Ordinance") + elif classname == "MediaRef": + ref = objsec.get_reference_handle() + obj = self.dbstate.db.get_object_from_handle(ref) + descr = obj.get_description().strip() + if not descr: + descr = obj.get_path() + if not descr: + descr = obj.get_mime_type() + classobj = _("Media Reference") + elif classname == "Name": + descr = _nd.display_name(objsec) + classobj = _("Name") + elif classname == "PersonRef": + ref = objsec.get_reference_handle() + person = self.dbstate.db.get_person_from_handle(ref) + if person is None: + descr = ref + else: + descr = _nd.display_name(person.get_primary_name()) + + descr = _("%(secobj)s: %(descr)s") % { + 'secobj': classobj, + 'descr' : descr} + return descr + + def setup_interface(self): + """ + Set all information on the widgets + * button tabs to load citation + * treeview in scrollable with info + """ + ##print (self.srtdata) + ##print(self.obj2citemap) + #create the load button, add it to a hbox, and add that box to the + #tab page + self.load_btn = SimpleButton(Gtk.STOCK_APPLY, self.load_button_clicked) + + hbox = Gtk.HBox() + hbox.set_spacing(6) + hbox.pack_start(self.load_btn, False, True, 0) + + hbox.show_all() + self.pack_start(hbox, False, True, 0) + if self.dbstate.db.readonly: + self.load_btn.set_sensitive(False) + + # create the tree, turn on rule hinting and connect the + # button press to the double click function. + self.tree = Gtk.TreeView() + self.tree.set_rules_hint(True) + self.tree.connect('button_press_event', self.double_click) + self.tree.connect('key_press_event', self.key_pressed) + + self.make_columns() + self.tree.set_model(self.model) + self.tree.expand_all() + + # create the scrolled window, and attach the treeview + scroll = Gtk.ScrolledWindow() + scroll.set_shadow_type(Gtk.ShadowType.IN) + scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scroll.add(self.tree) + #add this to the tab + self.pack_start(scroll, True, True, 0) + + def double_click(self, obj, event): + """ + Handles the double click on list. If the double click occurs, + the load button handler is called + """ + if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1: + self.load_button_clicked(obj) + + def key_pressed(self, obj, event): + """ + Handles the return key being pressed on list. If the key is pressed, + the Load button handler is called + """ + if event.type == Gdk.EventType.KEY_PRESS: + #print 'key pressed', event.keyval, event.get_state(), _ADD + if event.keyval in (_RETURN, _KP_ENTER): + self.load_button_clicked(obj) + return True + else: + return GrampsTab.key_pressed(self, obj, event) + return False + + def load_button_clicked(self, obj): + """ + Function called with the Load button is clicked. This function + should be overridden by the derived class. + """ + print("Uncaught Add clicked") + + def build_model(self): + """ + set up the model the treeview will use based on the data + """ + # store (citationhandle, primobjhandle, name, citationgid, index) + # here, depending on the leve, name will be primobjname, secobjname, or + # subsecobjname + # citationhandle will be '' for rows which create sublevels + self.model = Gtk.TreeStore(str, str, str, str, int) + for (descr, primhandle, shortdescr) in self.srtdata: + data = self.obj2citemap[primhandle] + #top level node + iter = self.model.append(None, ['', primhandle, descr, '', -1]) + for ind, chandle in enumerate(data['prim']): + citation = self.dbstate.db.get_citation_from_handle(chandle) + self.model.append(iter, [chandle, primhandle, shortdescr, + citation.get_gramps_id(), ind]) + base = len(data['prim']) + for ind, val in enumerate(data['sec']): + chandle, secdescr = val + citation = self.dbstate.db.get_citation_from_handle(chandle) + self.model.append(iter, [chandle, primhandle, secdescr, + citation.get_gramps_id(), base+ind]) + base += len(data['sec']) + for ind, val in enumerate(data['subsec']): + chandle, subsecdescr = val + citation = self.dbstate.db.get_citation_from_handle(chandle) + self.model.append(iter, [chandle, primhandle, subsecdescr, + citation.get_gramps_id(), base+ind]) + + def make_columns(self): + #make the columns in the treeview + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn(_("Cited in"), renderer, text=2) + self.tree.append_column(column) + column = Gtk.TreeViewColumn(_("Citation"), renderer, text=3) + self.tree.append_column(column) diff --git a/gramps/gui/editors/displaytabs/grampstab.py b/gramps/gui/editors/displaytabs/grampstab.py index 83a547ee5..0c867761b 100644 --- a/gramps/gui/editors/displaytabs/grampstab.py +++ b/gramps/gui/editors/displaytabs/grampstab.py @@ -118,8 +118,8 @@ class GrampsTab(Gtk.VBox): else: func = Gtk.Image.new_from_stock name = icon - - self.tab_image = func(name, Gtk.IconSize.MENU) + + self.tab_image = func(name, Gtk.IconSize.MENU) self.track_ref_for_deletion("tab_image") self.label = Gtk.Label(label=self.tab_name) self.track_ref_for_deletion("label") diff --git a/gramps/gui/editors/displaytabs/srctemplatetab.py b/gramps/gui/editors/displaytabs/srctemplatetab.py index 2b342e29c..5cf8db144 100644 --- a/gramps/gui/editors/displaytabs/srctemplatetab.py +++ b/gramps/gui/editors/displaytabs/srctemplatetab.py @@ -129,6 +129,33 @@ class SrcTemplateTab(GrampsTab): #a predefined template, self.reset_template_fields(srcattr.EVIDENCETEMPLATES[index]) + def _add_entry(self, row, srcattrtype, label): + """ + Add an entryfield to the grid of fields at row row, to edit the given + srcattrtype value. Use label label if given to indicate the field + (otherwise the srcattrtype string description is used) + Note srcattrtype should actually be the integer key of the type! + """ + self.gridfields.insert_row(row) + field = srcattrtype + #setup label + if not label: + srcattr = SrcAttributeType(field) + label = str(srcattr) + lbl = Gtk.Label(_("%s:") % label) + lbl.set_halign(Gtk.Align.START) + self.gridfields.attach(lbl, 0, row-1, 1, 1) + self.lbls.append(lbl) + #setup entry + inpt = UndoableEntry() + inpt.set_halign(Gtk.Align.FILL) + inpt.set_hexpand(True) + self.gridfields.attach(inpt, 1, row-1, 1, 1) + self.inpts.append(inpt) + MonitoredEntry(inpt, self.set_field, self.get_field, + read_only=self.dbstate.db.readonly, + parameter=srcattrtype) + def reset_template_fields(self, template): # first remove old fields for lbl in self.lbls: @@ -139,25 +166,9 @@ class SrcTemplateTab(GrampsTab): self.inpts = [] row = 1 # now add new fields - for fielddef in template[REF_TYPE_F]: - self.gridfields.insert_row(row) + for fielddef in template[REF_TYPE_L]: + self._add_entry(row, fielddef[1], '') row += 1 - field = fielddef[1] - #setup label - srcattr = SrcAttributeType(field) - lbl = Gtk.Label(_("%s:") %str(srcattr)) - lbl.set_halign(Gtk.Align.START) - self.gridfields.attach(lbl, 0, row-1, 1, 1) - self.lbls.append(lbl) - #setup entry - inpt = UndoableEntry() - inpt.set_halign(Gtk.Align.FILL) - inpt.set_hexpand(True) - self.gridfields.attach(inpt, 1, row-1, 1, 1) - self.inpts.append(inpt) - MonitoredEntry(inpt, self.set_field, self.get_field, - read_only=self.dbstate.db.readonly, - parameter=field) self.show_all() @@ -196,7 +207,6 @@ class SrcTemplateTab(GrampsTab): src.add_attribute(foundattr) #indicate source object changed self.callback_src_changed() - ## def setup_autocomp_combobox(self): ## """ diff --git a/gramps/gui/editors/editcitation.py b/gramps/gui/editors/editcitation.py index b9029ebb4..6c33d735b 100644 --- a/gramps/gui/editors/editcitation.py +++ b/gramps/gui/editors/editcitation.py @@ -298,7 +298,7 @@ class EditCitation(EditPrimary): self.db.readonly) self.author = MonitoredEntry( - self.glade.get_object('author'), self.source.set_author, + self.glade.get_object('author'), self.eat_it, self.source.get_author,self.db.readonly) self.gid = MonitoredEntry( @@ -323,9 +323,15 @@ class EditCitation(EditPrimary): self.source.get_abbreviation,self.db.readonly) self.pubinfo = MonitoredEntry( - self.glade.get_object('pub_info'), self.source.set_publication_info, + self.glade.get_object('pub_info'), self.eat_it, self.source.get_publication_info,self.db.readonly) + def eat_it(self, *pars): + """ + TODO: remove this method again, for prototype only + """ + pass + def _create_tabbed_pages(self): """ Create the notebook tabs and inserts them into the main diff --git a/gramps/gui/editors/editsource.py b/gramps/gui/editors/editsource.py index 6de9d9dcb..9fab397f1 100644 --- a/gramps/gui/editors/editsource.py +++ b/gramps/gui/editors/editsource.py @@ -54,7 +54,7 @@ from .editreference import RefTab from .editmediaref import EditMediaRef from .displaytabs import (NoteTab, GalleryTab, SrcAttrEmbedList, - SrcTemplateTab, + SrcTemplateTab, CitedInTab, CitationBackRefList, RepoEmbedList) from ..widgets import MonitoredEntry, PrivacyButton, MonitoredTagList from ..dialog import ErrorDialog @@ -286,7 +286,7 @@ class EditSource(EditPrimary): callback_notebase_changed=self.update_notes) self._add_tab(notebook, self.note_tab) self.track_ref_for_deletion("note_tab") - + self.gallery_tab = GalleryTab(self.dbstate, self.uistate, self.track, @@ -294,21 +294,26 @@ class EditSource(EditPrimary): self.load_source_image) self._add_tab(notebook, self.gallery_tab) self.track_ref_for_deletion("gallery_tab") - - self.attr_tab = SrcAttrEmbedList(self.dbstate, - self.uistate, - self.track, - self.obj.get_attribute_list()) - self._add_tab(notebook, self.attr_tab) - self.track_ref_for_deletion("attr_tab") - + self.repo_tab = RepoEmbedList(self.dbstate, self.uistate, self.track, self.obj.get_reporef_list()) self._add_tab(notebook, self.repo_tab) self.track_ref_for_deletion("repo_tab") - + + self.attr_tab = SrcAttrEmbedList(self.dbstate, + self.uistate, + self.track, + self.obj.get_attribute_list()) + self._add_tab(notebook, self.attr_tab) + self.track_ref_for_deletion("attr_tab") + + self.citedin_tab = CitedInTab(self.dbstate, self.uistate, + self.track, self.obj) + self._add_tab(notebook, self.citedin_tab) + self.track_ref_for_deletion("citedin_tab") + self.backref_list = CitationBackRefList(self.dbstate, self.uistate, self.track, diff --git a/gramps/gui/widgets/srctemplatetreeview.py b/gramps/gui/widgets/srctemplatetreeview.py index 78d6e9d73..4553ebee6 100644 --- a/gramps/gui/widgets/srctemplatetreeview.py +++ b/gramps/gui/widgets/srctemplatetreeview.py @@ -23,13 +23,19 @@ """ A class to select source templates """ +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +from gramps.gen.const import GRAMPS_LOCALE as glocale +_ = glocale.translation.gettext #------------------------------------------------------------------------- # # GTK classes # #------------------------------------------------------------------------- - from gi.repository import Gdk from gi.repository import Gtk @@ -38,7 +44,6 @@ from gi.repository import Gtk # Gramps classes # #------------------------------------------------------------------------- - from gramps.gen.lib import SrcAttributeType #------------------------------------------------------------------------- @@ -79,7 +84,7 @@ class SrcTemplateTreeView(Gtk.TreeView): self.Str2I = srcattrt.S2I_SRCTEMPLATEMAP self.Key2I = srcattrt.K2I_SRCTEMPLATEMAP self.Key2Path = {} - # store (index, key, cat, cat_type, src_type) + # store (index, key, src_type) self.model = Gtk.TreeStore(int, str, str) alltexts = sorted(self.Str2I.keys()) parentiter = None @@ -136,7 +141,7 @@ class SrcTemplateTreeView(Gtk.TreeView): def make_columns(self): #make the column in the treeview renderer = Gtk.CellRendererText() - column = Gtk.TreeViewColumn("Template", renderer, text=2) + column = Gtk.TreeViewColumn(_("Template"), renderer, text=2) self.append_column(column) #no headers needed: self.set_headers_visible (False)