Making event label visible

in person category I can give the person a label and make it visible as a column
When I ad an event to the person I can give the event a label but it isn’t visible in the event-list that is in de same layout. [person-category > events]
It seems it is only visible from within the events category or when I open up that specific event.
Is it possible to make the label of the event visible from the event-list within the person category?
Win 11
AIO64-6.0.8–1

You can.. unless we are talking about different things.

And we are talking about different things.

You want the Tags of an event to appear in the Events gramplet for the People and Families views!!!

Correct Dave, Not what the upper arrow indicates, but the lower one in this screendump.
Is it possible?

You would have to hack the Events gramplet since those gramplets do do not have the optional column selector and ordering which is offered in the Configure for list view modes.

But you could use the Events category list view as a template for adding another column.

Expanded @ursus idea and asked Claude to propose a Events+ addon gramplet:

“Events+” Gramplet — Design Document

Status: Design/feasibility — not yet implemented.
Reference sources (gramps-project/gramps@master):
gramps/plugins/view/eventview.py, gramps/gui/views/treemodels/eventmodel.py,
gramps/gui/views/listview.py, gramps/gui/views/pageview.py,
gramps/gen/utils/configmanager.py, gramps/plugins/view/relview.py,
gramps/plugins/gramplet/citations.py, gramps/plugins/view/view.gpr.py,
kkujansuu/gramps/add-multiple-events

1. Goal

A gramplet, embeddable in the Person, Relationships, Families, and Place views’ sidebar/
bottombar, that has the same layout as the Events Category (an Events-view-style list) — same columns, same order, same widths, same context-menu actions — but pre-filtered to the events relevant to the host view’s current object(s) instead of the whole database.

Added functionalities:

  • adds ID, Private, Tags, Last changed column access to current : Type, Description, Date, Age, Place, Main participants, Role
  • context menu actions: Add, Edit, Delete, Merge, QuickView
  • extended selection
  • Copy to clipboard
  • drag’n’drop support

2. Non-goals / explicit constraints

  • No separate toolbar inside the gramplet.
  • No “Configure” tab for the gramplet itself — it always mirrors the Events view’s own column configuration.
  • No new filter UI — filtering is driven entirely by the host view’s active
    object(s), not by a EventSidebarFilter.

3. Base class

Subclass Gramplet (gramps/gen/plug/_gramplet.py), not ListView/PageView.
ListView bundles a Gtk.UIManager, action groups, and toolbar XML that a
Gramplet host does not own and should not try to acquire. Build a plain
Gtk.TreeView in build_gui(), following the pattern already used by core
gramplets such as Citations (gramps/plugins/gramplet/citations.py).

selection = self.treeview.get_selection()
selection.set_mode(Gtk.SelectionMode.MULTIPLE)

gives extended selection with no custom code.

4. Row model: reuse EventModel via skip, not a new filter class

EventModel(db, uistate, skip=set()) already implements cursor iteration, column mapping (including the cached Main Participants column), and sorting for every column the Events view exposes. Its skip parameter is a set of handles to exclude.

Reuse strategy:

all_handles = set(db.get_event_handles())
wanted = self._compute_wanted_handles()   # context-specific, see §6
skip = all_handles - wanted
model = EventModel(db, uistate, skip=skip)

This reuses the entire row-building/sorting pipeline unmodified. No new Rule/GenericFilter subclass is needed, and none should be written — that would duplicate logic FlatBaseModel already owns.

Rebuild the model (new skip set) whenever:

  • the host view’s active object changes (active-changed signal), or
  • an event-add / event-update / event-delete / person-update / family-update signal arrives (same signal_map entries EventView already registers — reuse the same dict).

5. Column configuration sync

PageView.init_config() calls:

self._config = config.register_manager(self.ident, use_config_path=True)

register_manager() stores the ConfigManager in a global, class-level
registry
: ConfigManager.PLUGINS[name]. This means:

  • If the Events view has already been instantiated this session, calling config.get_manager("gramps.plugins.view.eventview") returns the same live object the Events view reads and writes. ConfigManager.connect(key, func) can subscribe directly to columns.visible / columns.rank / columns.size changes for true same-session live sync.
  • register_manager() has no “already registered” guard — it always creates a new ConfigManager and overwrites the global slot. If the gramplet calls it before the Events view has ever been opened this session, and the Events view is opened later, the Events view’s ownself._config reference is a different Python object than the one the gramplet cached earlier — they will drift apart.

Recommended pattern:

def _get_event_view_config():
    ident = "gramps.plugins.view.eventview"
    try:
        return config.get_manager(ident)
    except AttributeError:
        # Events view not instantiated yet this session — read-only,
        # do NOT call register_manager() (would collide with the
        # real view's later registration).
        return _read_only_config_from_ini(ident)

Never cache the manager reference across rebuilds — refetch immediately before reading columns. The lookup is cheap (dict access), and refetching sidesteps the load-order hazard entirely.

6. Context filtering per host view

6a. Person view

wanted = set(h.ref for h in person.get_event_ref_list())
for fam_handle in (person.get_family_handle_list()
                   + person.get_parent_family_handle_list()):
    family = db.get_family_from_handle(fam_handle)
    wanted.update(h.ref for h in family.get_event_ref_list())

Covers personal events, events of families where the person is a parent, and events of families where the person is a child.

6b. Place view

wanted = set(
    handle for (obj_class, handle) in
    db.find_backlink_handles(place.handle, include_classes="Event")
)

This is the same find_backlink_handles call EventView.tag_updated() already uses internally — direct reuse, not reinvention.

Open design question (not blocking): whether to also include events of places enclosed by/enclosing the active place via PlaceRef hierarchy.
Recommend v1 = exact place only, matching the literal spec.

6c. Relationships view — two options, decide during implementation

RelationshipView.collapsed_items (in relview.py) is a plain, private, non-persisted, per-instance dict — no public getter, no signal on toggle, does not survive between sessions. This is the one part of the spec that doesn’t map onto an existing public API.

Option A — introspect now (fragile, works today)

Reach uistate.viewmanager to find the live RelationshipView instance for the active person’s category page, and read collapsed_items / check_collapsed() directly to determine which families/people are currently expanded.

  • Pro: matches the spec exactly, works immediately.
  • Con: depends on a private attribute name/shape that isn’t part of any contract; a relview.py refactor silently breaks this gramplet with no deprecation warning; likely to draw review pushback if ever proposed for gramps-project/addons-source or core.

Option B — scoped v1 + upstream API follow-up (robust)

v1 shows events of the active person + their immediate families (as parent and as child) — same computation as §6a — regardless of what’s actually expanded/collapsed in the Relationships view’s UI. True expand-state tracking becomes a follow-up patch that adds to relview.py:

def get_expanded_families(self):
    """Return handles of families currently shown expanded."""
    ...

# emit a signal (or call a registered callback) from
# expand_collapse_press() so listeners can react live
  • Pro: no dependency on private state; a clean, reviewable upstream contribution that benefits any future consumer, not just this gramplet.
  • Con: v1 relationships-view filtering is coarser than the original spec until the upstream piece lands.

Recommendation: prototype with Option A locally to validate the rest of the design (columns, menu, add/merge) end-to-end, but plan to submit Option B’s small relview.py API addition upstream before proposing Events+ for inclusion in addons-source. Decide which ships in v1 once the rest of the gramplet is working and it’s clear whether the upstream patch is likely to land in a reasonable timeframe.

6d. Families view

The active object here is a Family, not a Person, so the wanted set is built directly from the family and its two parents — no backlink lookup or private-state introspection needed, unlike §6b/§6c:

family = db.get_family_from_handle(active_family_handle)
wanted = set(h.ref for h in family.get_event_ref_list())

for parent_handle in (family.get_father_handle(), family.get_mother_handle()):
    if parent_handle:
        parent = db.get_person_from_handle(parent_handle)
        wanted.update(h.ref for h in parent.get_event_ref_list())

This covers the family’s own events (e.g. Marriage, Divorce — attached via Family.event_ref_list with EventRoleType.FAMILY, per §8.2’s attachment convention) plus each parent’s personal events (Birth, Death, etc.).

Open design question, same shape as the Person-view one in §6a: whether to
also include the children’s personal events. The literal spec text for this view (family members’ events) doesn’t explicitly rule children in or out the way §6a’s “child or parent” wording does for the Person view.
Recommend v1 = family + both parents only, matching the Family-context variant of add-multiple-events (§8.1), which likewise scopes to Father/Mother/Children as attachable targets for sharing but doesn’t imply children’s own personal events belong in an at-a-glance Family event list.
Children’s events remain one filter checkbox/toggle away if this proves too narrow in practice — not a blocking decision for v1.

Signal wiring mirrors §6a: rebuild on active-changed for the Family navigation type, plus the same event-*/family-update/person-update entries from EventView’s signal_map (family and parent-person changes both need to trigger a re-filter, since either can add/remove an event from the wanted set without an event-* signal firing on its own).

7. Context menu — cannot literally reuse the Events view’s UIManager XML

win.Add / win.Edit / win.Remove / win.Merge in eventview.py’s additional_ui are actions on the main window’s action group, which is swapped per-category by whichever View is currently active. A gramplet embedded in, say, the Person view does not own that action group — the Person view does. Invoking win.Add from inside the gramplet would add a Person, not an Event.

Resolution: build a small hand-rolled Gtk.Menu inside the gramplet (right-click + Menu key), mirroring the Events view’s Popup section:

  • Add…, Edit…, Delete, Merge… — same labels/icons as eventview.py.
  • Sensitivity rules, dimmed per selection count:
    • Edit: enabled for 1+ rows.
    • Delete: enabled for 1+ rows.
    • Merge: enabled only when exactly 2 rows selected (matches EventView.merge()'s existing requirement verbatim).
    • Add: always enabled; behavior depends on number of context targets, not treeview row selection (see §8).

Every action underneath is fully reused: gui.editors.EditEvent, gui.merge.MergeEvent, remove_selected_objects-equivalent handle-based deletion, tag add/remove via db.commit_event. Only the menu wiring is new; none of the underlying editors/dialogs/transaction logic is reimplemented.

8. “Add & share” across multiple context targets

“Target” here means the Person/Family objects providing the filter context — not rows in the gramplet’s own treeview. This matters most in the Relationships-view case, where multiple families/people can be in context simultaneously.

8.1 Prior art: kkujansuu/gramps/addons/add-multiple-events

add-multiple-events.py (Kari Kujansuu, GPL-2.0-or-later, same author as the Isotammi addons) is a QUICKREPORT registered against CATEGORY_QR_FAMILY and CATEGORY_QR_PERSON that solves a closely related problem: sharing one event across a Family’s Father/Mother/Children, or across a Person’s parents/spouses/children. Several of its patterns are worth adopting directly, one detail should be deliberately not copied, and one behavior differs from what this spec actually asks for.

Adopt:

  • Opt-out checklist, not a blind broadcast. It builds a pre-checked Gtk.CheckButton per target (Father/Mother/Child/Spouse as applicable), plus a “check/clear all” toggle, and only applies the event to whichever boxes are still checked when the user confirms. Events+ should do the same for its multi-target Add: show the context targets as an opt-out list rather than fanning out unconditionally.
  • EditEventRef, not bare EditEvent, for both creating a new event and attaching an existing one — it lets the user set/confirm the Role at the same moment, via EditEventRef(dbstate, uistate, [], event, ref, callback).
    This is a better fit than editing the Event and assigning roles as a separate step.
  • Two entry points, one callback: “Select Event” (SelectorFactory ('Event'), then EditEventRef to attach a role) and “New Event” (blank Event() with event.new_event = True), both funneling into the same eventref_callback(eventref, event).
  • Persisted defaults: config.register_manager("events-plus") with keys for last-used event type/role/place, so repeated “Add & share” actions don’t require re-picking the same type/role every time. Mirrors §5’s ConfigManager usage, just a separate manager/ident for the gramplet’s own transient state (not the Events-view column config, which must stay untouched per §5).

Do not adopt: the addon’s runtime widget surgery to detect “am I a real Quick View dialog or embedded in the Quick View gramplet host” (swapping TextView/ScrolledWindow/VBox contents at runtime). That workaround exists only because a QUICKREPORT is being retrofitted into the generic Quick-View gramplet shell. Events+ is a Gramplet from the start (§3), so this entire class of hack doesn’t apply — build the Gtk.TreeView/dialog normally.

Deliberate divergence — role assignment and attachment point: the reference addon always resolves targets down to individual Person handles and always calls person.add_event_ref() — even when invoked from the Family context menu, Father/Mother are still attached as Person-level refs, never as a Family.event_ref_list entry. That’s a reasonable simplification for its own scope, but it does not match what this spec asks for (“share … using the Primary or Family event role as appropriate”), which implies Family-object targets should get a FAMILY-role EventRef on the family itself, and Person-object targets a PRIMARY-role EventRef on the person. Events+ should keep the differentiated attachment from §8.2 below rather than the addon’s person-only simplification.

Optional, not required for v1: the addon supports both “share” (one Event, multiple refs) and “copy” (independent Event per additional target, with a RESIDENCE-specific date-clamping correction to the target’s birth/death range) via a share checkbox. The spec here asks for “share,” so v1 doesn’t need copy-mode, but it’s a documented option if a future iteration wants it.

8.2 Events+ implementation, incorporating the above

event = Event()
ref = EventRef()
try:
    EditEventRef(dbstate, uistate, [], event, ref, cb_event_added)
except WindowActiveError:
    return

def cb_event_added(self, eventref, event):
    """Called by EditEventRef once the user confirms the new/selected
    event. Present the opt-out target checklist before committing refs."""
    self._show_target_checklist(event, context_targets)

# after the user confirms the (possibly trimmed) target list:
with DbTxn(_("Add shared event"), db) as trans:
    for target in confirmed_targets:
        eventref = EventRef()
        eventref.ref = event.handle
        if isinstance(target, Family):
            eventref.set_role(EventRoleType(EventRoleType.FAMILY))
            target.add_event_ref(eventref)
            db.commit_family(target, trans)
        else:  # Person
            eventref.set_role(EventRoleType(EventRoleType.PRIMARY))
            target.add_event_ref(eventref)
            db.commit_person(target, trans)

This is the one piece of genuinely new orchestration logic in the whole design — everything it calls (EventRef, EventRoleType, EditEventRef, DbTxn, commit_person/commit_family) is stock gen.lib/gen.gui/gen.db API, and the UX shape (checklist, persisted defaults, two entry points) is lifted from a working, GPL-2.0-or-later precedent rather than designed from scratch.

9. Signals / live updates

Reuse EventView.__init__'s signal_map dict verbatim as the basis for dbstate.db.connect(...) wiring inside the gramplet, plus:

  • active-changed on the host view’s navigation type (Person/Place), via Gramplet.connect_signal(nav_type, method) — already built into the Gramplet base class.
  • For Relationships view Option A only: whatever hook is used to observe expand_collapse_press (polling on active-changed is an acceptable fallback if no signal is available).

10. Risk summary

Area Risk Mitigation
Column sync register_manager load-order race Refetch via get_manager each rebuild; never cache long-term
Context menu Can’t reuse win.* actions Hand-rolled Gtk.Menu, same underlying editors
Relationships filter Private, non-persisted state Option A (fragile) vs Option B (scoped + upstream API) — decide at implementation time
Multi-target add New orchestration logic UX pattern (checklist, EditEventRef, persisted defaults) adapted from kkujansuu/gramps/add-multiple-events; attachment logic (Person vs Family role) is Events±specific, built on stock EventRef/DbTxn APIs
Merge None Verbatim reuse of EventView.merge() logic (exactly 2 selected)

11. Phased plan

  1. v1 skeleton: Gramplet + Gtk.TreeView + EventModel via skip, Person-view and Place-view filtering (§6a/§6b), config sync (§5), hand-rolled context menu (§7), single/merge actions.
  2. v1.1: multi-target “Add & share” (§8).
  3. v2: Relationships-view filtering — Option A prototype first, propose Option B’s relview.py API upstream in parallel, switch over once available.

12. AI-disclosure note (per project guidelines)

Per Howto:_Contribute_to_Gramps#AI_generated_code, once implementation begins: commit messages must name the AI tool/provider/version used, substantially-AI-written commits need a Generated-by: tag with an indication of the prompts used, and any AI-assisted review/pairing needs Co-authored-by:. This design doc itself should be referenced from the first implementation commit’s Generated-by: tag as the guideline document that was worked against.

O wow, not that I understand anything (or mostly not)

Just being proud on my own gram.py script I made
All 390 lines of it. It does the job I wanted (Still wondering how to solve some things, like: is it possible the script will ‘know’ what the active person record is. Instead of me needing to fill it manually all the time…)
So back to the answer to my question: ‘no, it is currently not possible to show the label where you want it’

You could look at some other Gramplets that track the Active Person.

The Deep Gallery gramplet by Hans Boldt @ukulelehans is a 7k of code example:

mis-read the posting. Here’s the gram.py way to reference the Active record in the categories:

Thank you Brian.

I managed to do that
used: active_handle = self.get_active(‘Person’)
and even slimmed the script down by 30 lines because of that.

Nice!

But if you are not going to to adapt your Gramp.py script to be wrapped in an addon interface, you would not need to “declare” it at all.

You could just use the Gram.py pre-declared active_person directly.

I am just mightily releaved I have managed this.
Will look at this at a later date. Right. It works now..
Thank you again.

@ursus would you mind sharing your Gram.py script (if it doesn’t contain personal bits)? I don’t yet have many examples available for people. I am so happy you were able to write one!

I could do that,

But it is all geared to giving a result text in Dutch.
what it does:

For the currently active person
It checks if it a woman. (You’ll see why)
Then checks if she has had children out of wedlock
If so the scripts lists those children

It also checks if the woman got any children within
280 after marriage (Average gestiation period)
Displaying the number of days she was probably pregnant

Now I now the calculations used are not very scientific
I wanted to know this to see if further (more exact) statistics would yield anything more valuable for the research object.
Which includes number of children, ages gaps between children, age gaps between first and last, ages of parent (or both parents if both are known)
And later correlation will follow between places where they live, income etc.
But all these are in the future now.

So it is a tool for researchers (Two other databases other then mine are involved) to gain some insight if detailed research would be warranted or be a waiste of time and effort.

Also would you need it to be translated?
And I wouldn’t want it being public right now.

Kind regards,
Erik Appeldoorn

You could share it with @dsblank via Discourse Private Messaging. He could see if it inspires him to create any sample Gram.py scripts. Ones that are more broadly useful (and understandable, or inspirational) for the general public.