我想用 Python 编写一个可以与 交互的日历应用程序gnome-shell-calendar
。我四处打听,有人告诉我它使用evolution-data-sever
为了获取其信息,我发现有一个python-evolution
Python 模块可以让你与进化服务器交互。但是,该模块现在已经折旧还有其他方式与服务器交互吗?
我还注意到一个过程叫做gnome-shell-calendar-server
。那和进化版有什么区别?
答案1
Evolution Data Server 3.6 可通过 Python 使用 gobject 自检来访问。为此,gir1.2-edataserver-1.2
还gir1-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,则只需要pgi
pip 包。以下是如何使其与 Conda 配合使用:
- 我曾经
conda
创建过虚拟环境,因此安装了pgi
pip 包 - 我安装了
libgirepository
和pygobject
使用 conda-forge。 - 我将所需的 typelib 文件
gir1.2-ecalendar-1.2
与gir1.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()
现在,您要格外小心处理外面的事件,因为您也可以编辑它们,不要破坏您的日历;)