使用 freedesktop 门户使用 Python 截屏

Posted

技术标签:

【中文标题】使用 freedesktop 门户使用 Python 截屏【英文标题】:Using freedesktop portal to take screenshots with Python 【发布时间】:2019-10-15 12:21:15 【问题描述】:

我一直在尝试用 python 编写一个 flatpak 应用程序,它使用 freedesktop 门户截取屏幕截图。我正在调整 Gimp 的 freedesktop screenshot 插件,它是用 c 编写的,在获取截图并与我的应用程序共享后,我遇到了麻烦。我得到一个 '/org/freedesktop/portal/desktop/request/1_326/t' 形式的 uri,freedesktop 门户文档说 uri 指向安装在 run/user/$UID/doc/ 和我可以确认屏幕截图已保存在那里。但是,我似乎无法为它们获取有用的标识符,以便我可以将它们附加到我的应用程序的电子邮件中。

我已经尝试过 freedesktop Documents 门户中的所有方法,但都没有成功。在调用一个 dbus 代理后,该代理返回一个保存在 opath 变量中的 uri,Gimp 实现设置另一个 dbus 代理,然后在此处将该代理连接到回调函数:我从中复制这些 sn-ps 的完整文件可在此处获得 https://github.com/GNOME/gimp/blob/master/plug-ins/screenshot/screenshot-freedesktop.c>

if (opath)
    
      GDBusProxy *proxy2 = NULL;

      proxy2 = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                              G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
                                              NULL,
                                              "org.freedesktop.portal.Desktop",
                                              opath,
                                              "org.freedesktop.portal.Request",
                                              NULL, NULL);
      *image_ID = 0;
      g_signal_connect (proxy2, "g-signal",
                        G_CALLBACK (screenshot_freedesktop_dbus_signal),
                        image_ID);

      gtk_main ();
      g_object_unref (proxy2);
      g_free (opath);

screenshot_freedesktop_dbus_signal 函数如下所示:

static void
screenshot_freedesktop_dbus_signal (GDBusProxy *proxy,
                                    gchar      *sender_name,
                                    gchar      *signal_name,
                                    GVariant   *parameters,
                                    gint32     *image_ID)

  if (g_strcmp0 (signal_name, "Response") == 0)
    
      GVariant *results;
      guint32   response;

      g_variant_get (parameters, "(u@asv)",
                     &response,
                     &results);

      /* Possible values:
       * 0: Success, the request is carried out
       * 1: The user cancelled the interaction
       * 2: The user interaction was ended in some other way
       * Cf. https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.Request.xml
       */
      if (response == 0)
        
          gchar *uri;

          if (g_variant_lookup (results, "uri", "s", &uri))
            
              GFile *file = g_file_new_for_uri (uri);
              gchar *path = g_file_get_path (file);

              *image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
                                          path, path);
              gimp_image_set_filename (*image_ID, "screenshot.png");

              /* Delete the actual file. */
              g_file_delete (file, NULL, NULL);

              g_object_unref (file);
              g_free (path);
              g_free (uri);
            
        

      g_variant_unref (results);
      gtk_main_quit ();
    

在回调函数之前,我的代码的工作方式相同。我找不到 g_signal_connect 函数的 pythonic 等效项。我试过像这样将回调添加到新代理的初始化中

proxy2 = Gio.DBusProxy.new_for_bus(Gio.BusType.SESSION,
                                            Gio.DBusProxyFlags.NONE,
                                            None,
                                            "org.freedesktop.portal.Desktop",
                                            returned_uri[0],
                                            "org.freedesktop.portal.Request",
                                            None,
                                            G_CALLBACK(self.receive_screenshot_signal),
                                            None)

但是我的 receive_screenshot_signal 函数没有传递任何值,并且传入的任务对象说它们没有完成。

我不知道从哪里开始,所以任何关于如何更有效地使用 dbusproxy 库和/或 freedesktop 门户的建议或见解将不胜感激。谢谢!

【问题讨论】:

【参考方案1】:

我通过订阅请求句柄路径上来自总线的信号解决了这个问题。

        args = GLib.Variant('(sasv)', (filename, ))
        result = self.proxy.call_sync('Screenshot',
                                      args,
                                      Gio.DBusCallFlags.NONE,
                                      -1,
                                      None)
        request_handle = result.unpack()[0]
        self.bus.signal_subscribe("org.freedesktop.portal.Desktop",
                                  "org.freedesktop.portal.Request",
                                  "Response",
                                  request_handle,
                                  None,
                                  Gio.DBusSignalFlags.NO_MATCH_RULE,
                                  self.receive_screenshot_signal)

我在 Gio.bus.signal_subscribe 上找不到任何具体的文档,但这里是回调函数如何处理返回:

  def receive_screenshot_signal(self,
                                  connection,
                                  sender,
                                  path,
                                  interface,
                                  signal,
                                  params):
        if not isinstance(params, GLib.Variant):
            print("It's not a variant")
            return
        response_code, results = params.unpack()
        # Response code 0 signifies success
        if response_code != 0 or 'uri' not in results:
            print("Error taking screenshot")
            return
        parsed_uri = urlparse(results['uri'])
        assert parsed_uri.scheme == "file"
        self.screenshot = unquote(parsed_uri.path)

【讨论】:

以上是关于使用 freedesktop 门户使用 Python 截屏的主要内容,如果未能解决你的问题,请参考以下文章

java中的freedesktop.org通知

调用 RegisterProfile 时出现 org.freedesktop.DBus.Error.UnknownMethod 错误

QDBus 问题:获取 org.freedesktop.DBus.Error.UnknownMethod,但方法存在

我们啥时候应该使用 SNOWPIPE?

在linux中网络管理器的接口“org.freedesktop.NetworkManager.Device”上生成的信号名称

从 DBUS org.freedesktop.dbus 和 java 获取数据 - org.freedesktop.DBus$Error$UnknownMethod: 方法不存在