为 Gnome-shell 日历 / Evolution 日历编写自己的应用程序

为 Gnome-shell 日历 / Evolution 日历编写自己的应用程序

我想用 Python 编写一个可以与 交互的日历应用程序gnome-shell-calendar。我四处打听,有人告诉我它使用evolution-data-sever

为了获取其信息,我发现有一个python-evolutionPython 模块可以让你与进化服务器交互。但是,该模块现在已经折旧还有其他方式与服务器交互吗?

我还注意到一个过程叫做gnome-shell-calendar-server。那和进化版有什么区别?

答案1

Evolution Data Server 3.6 可通过 Python 使用 gobject 自检来访问。为此,gir1.2-edataserver-1.2gir1-2-ecalendar-1.2需要安装 和 。

例如,以下脚本将列出 evolution-data-server 中所有日历中的所有事件。

#! /usr/bin/python
# -*- coding: utf-8 -*-

from gi.repository import ECalendar
from gi.repository import EDataServer

# Open a registry and get a list of all the calendars in EDS
registry = EDataServer.SourceRegistry.new_sync(None)
sources = EDataServer.SourceRegistry.list_sources(registry, EDataServer.SOURCE_EXTENSION_CALENDAR)

# Open each calendar containing events and get a list of all objects in them
for source in sources:
    client = ECalendar.CalClient.new(source, ECalendar.CalSourceType.EVENT)
    client.open_sync(False, None)

    # ret is true or false depending if events are found or not
    # values is a list of events
    ret, values = client.get_object_list_as_comps_sync("#t", None)
    if ret:
        for value in values:
            event = value.get_as_string()
            print event

答案2

马克的回答在 2012 年可能还不错,但现在有点过时了。我想试试这个,经过自己的挖掘,终于做到了。

套餐

这是在 Ubuntu 20.04 上测试的,安装了以下软件包:

sudo apt update
sudo apt install -y \
    gir1.2-ecalendar-1.2 \
    gir1.2-ecal-2.0

Conda 环境设置(可选)

如果你使用系统的 Python,则只需要pgipip 包。以下是如何使其与 Conda 配合使用:

  1. 我曾经conda创建过虚拟环境,因此安装了pgipip 包
  2. 我安装了libgirepositorypygobject使用 conda-forge。
  3. 我将所需的 typelib 文件gir1.2-ecalendar-1.2gir1.2-ecal-2.0我的 conda env 的girepository文件夹进行符号链接,如下所示:
#!/usr/bin/env bash

set -ex

girepository_source=/usr/lib/x86_64-linux-gnu/girepository-1.0
conda_env_name=evolution-calendar-playground
conda_girespository_destination=${CONDA_PREFIX}/envs/${conda_env_name}/lib/girepository-1.0/

# setup conda environment
conda activate ${conda_env_name}
pip install pgi
conda install -c conda-forge libgirepository -y
conda install -c conda-forge pygobject -y

# compare the girepository folders
diff -rq "${girepository_source}" "${conda_girespository_destination}"

# link only required typelib files
ln -s "${girepository_source}/EDataServer-1.2.typelib" "${conda_girespository_destination}"
ln -s "${girepository_source}/Soup-2.4.typelib" "${conda_girespository_destination}"
ln -s "${girepository_source}/Camel-1.2.typelib" "${conda_girespository_destination}"
ln -s "${girepository_source}/GData-0.0.typelib" "${conda_girespository_destination}"
ln -s "${girepository_source}/Json-1.0.typelib" "${conda_girespository_destination}"
ln -s "${girepository_source}/Goa-1.0.typelib" "${conda_girespository_destination}"
ln -s "${girepository_source}/ECal-2.0.typelib" "${conda_girespository_destination}"
ln -s "${girepository_source}/ICalGLib-3.0.typelib" "${conda_girespository_destination}"

# alternative version of the above, link all files that are missing
typelibs_not_in_conda_env=$(
    diff -rq "${girepository_source}" "${conda_girespository_destination}" \
    | grep "Only in ${girepository_source}" \
    | cut -d " " -f 4-
    | tr "\n" " "
)

for typelib in $(echo ${typelibs_not_in_conda_env} | tr " " "\n"); do
    ln -s "${girepository_source}/${typelib}" "${conda_girespository_destination}"
done

# compare the girepository folders (again)
diff -rq "${girepository_source}" "${conda_girespository_destination}"

更新了 Python 示例,以检索 Evolution Data Server 中所有日历中的所有事件

这是一个更新的 Python 脚本,它使用gobject 自省(它高度基于 Mark 的示例)。它的功能与之前相同,但写法不同,并且已更新为gir1.2-ecal-2.0使用gir1.2-ecalendar-1.2

我链接了一些文档,以便更容易了解发生了什么,并添加了一些print语句,因为这些库是动态加载的,而且很难获得它们的智能。获得所需的内容后,请随意清理所有内容。

此外,根据文档,我认为我打开的一些连接应该被清理,但我没有这样做,但你应该:)

import gi

gi.require_version('EDataServer', '1.2')
from gi.repository import EDataServer

gi.require_version('ECal', '2.0')
from gi.repository import ECal

from gi.repository import Gio

# https://lazka.github.io/pgi-docs/Gio-2.0/classes/Cancellable.html#Gio.Cancellable
GIO_CANCELLABLE = Gio.Cancellable.new()


class EvolutionCalendarWrapper:
    @staticmethod
    def _get_gnome_calendars():
        # https://lazka.github.io/pgi-docs/EDataServer-1.2/classes/SourceRegistry.html#EDataServer.SourceRegistry.new_sync
        registry = EDataServer.SourceRegistry.new_sync(GIO_CANCELLABLE)
        return EDataServer.SourceRegistry.list_sources(registry, EDataServer.SOURCE_EXTENSION_CALENDAR)

    def _get_gnome_events_from_calendar_source(self, source: EDataServer.Source):
        print(source.get_display_name())

        # https://lazka.github.io/pgi-docs/ECal-2.0/classes/Client.html#ECal.Client
        client = ECal.Client()

        # https://lazka.github.io/pgi-docs/ECal-2.0/classes/Client.html#ECal.Client.connect_sync
        new_client = client.connect_sync(
            source=source,
            source_type=ECal.ClientSourceType.EVENTS,
            wait_for_connected_seconds=1,  # this should probably be configured
            cancellable=GIO_CANCELLABLE,
        )

        if new_client:
            events = []
            # https://lazka.github.io/pgi-docs/ECal-2.0/classes/Client.html#ECal.Client.get_object_list_as_comps_sync
            ret, values = new_client.get_object_list_as_comps_sync(sexp="#t", cancellable=GIO_CANCELLABLE)
            if ret:
                for value in values:
                    print(value.get_status())
                    print(value.get_location())
                    print(value.get_descriptions())
                    print("due timestamp:", value.get_due().get_value().as_timet())
                    start_time_value = value.get_dtstart().get_value()
                    print("start_date", start_time_value.get_date())
                    print("start_time", start_time_value.get_time())
                    print("timestamp:", start_time_value.as_timet())
                    print("as_timet_with_zone:", start_time_value.as_timet_with_zone())
                    print("timezone:", start_time_value.get_timezone())
                    # event_str = value.get_as_string()
                    # print(event_str)
                    event = value
                    events.append(event)
        return events

    def get_all_events(self):
        calendars = self._get_gnome_calendars()
        events = []
        for source in calendars:
            events += self._get_gnome_events_from_calendar_source(source)
        return events


def main():
    evolutionCalendar = EvolutionCalendarWrapper()
    events = evolutionCalendar.get_all_events()
    for event in events:
        print(event)


if __name__ == "__main__":
    main()

现在,您要格外小心处理外面的事件,因为您也可以编辑它们,不要破坏您的日历;)

相关内容