从QML窗口使用Python创建Google日历事件

2024-09-27 07:25:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我正试图通过按钮单击上的多个qml文本字段将事件添加到我的google日历api中。首先,我肯定会对我的方法提出建设性的批评

我不是100%确定我当前的错误在哪里,但我遇到了两个错误,我认为这是我问题的根源,我感谢任何建议

我认为我在尝试“重用”AddToCalendar类中的self.service时导致了一个错误。这是我从另一个类中提取实例变量的时候。 我的错误是这样的(我的第二个错误替换了这个错误):Error:'NoneType' object has no attribute 'events'在我的Cal2.qml中的cal2.createevent(eventinfo.text, eventstart.text, eventend.text)

我的第二个也是最新的错误(在同一行)是在从多个文本字段单击按钮时向createevent发送信息 TypeError: createevent() missing 2 required positional arguments: 'eventstart' and 'eventend'

我已经包含了一些相关的代码片段,如果有更多的帮助,请告诉我

Cal2.py

class CalendarBackend(QtCore.QObject):
    eventsChanged = QtCore.Signal(list)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._service = None

    @property
    def service(self):
        return self._service

class AddToCalendar(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._data = dict()
        self.A = CalendarBackend()
    @QtCore.Slot(str)
    def createevent(self, eventinfo: str, eventstart: str, eventend: str):
        starttime = str(datetime.datetime.strptime(eventstart, "%m/%d/%Y %H:%M:%S"))
        endtime = str(datetime.datetime.strptime(eventend, "%m/%d/%Y %H:%M:%S"))
        try:
            event = {
                'summary': eventinfo,
                'start': {
                    'dateTime':  (starttime[0:10]+"T"+starttime[11:]+"-06:00"),
                    'timeZone': 'America/Chicago',
                },
                'end': {
                    'dateTime': (endtime[0:10]+"T"+endtime[11:]+"-06:00"),
                    'timeZone': 'America/Chicago',
                }
            }
        except:
            pass
        event = self.A._service.events().insert(calendarId='primary', body=event).execute()
        print('Event created: %s' % (event.get('htmlLink')))

if __name__ == "__main__":
    app = QtGui.QGuiApplication(sys.argv)

    QtQml.qmlRegisterType(CalendarProvider, "MyCalendar", 1, 0, "CalendarProvider")
    cal2 = AddToCalendar()
    engine = QtQml.QQmlApplicationEngine()
    engine.rootContext().setContextProperty("cal2", cal2)
    filename = os.path.join(CURRENT_DIR, "Calendar2.qml")
    engine.load(QtCore.QUrl.fromLocalFile(filename))

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

Cal2.qml

TextField {
    id: eventstart
    placeholderText: qsTr("Start Time 01/12/2020 14:35:00")
    selectByMouse: true
}
TextField {
    id: eventend
    placeholderText: qsTr("End Time 01/12/2020 16:35:00")
    selectByMouse: true
}
TextField {
    id: eventinfo
    placeholderText: qsTr("Event Name")
    selectByMouse: true
}
Butt {
    id: buttonn
    width: rect.width
    height: rect.height

    Rectangle {
        id: rect
        implicitWidth: rect.width
        implicitHeight: 25
    }

    Text {
        text: "Add Event"
    }


    onTouched: {
        console.log("touched")
        cal2.createevent(eventinfo.text, eventstart.text, eventend.text)
    }

Tags: textselfidinitdef错误serviceqml
1条回答
网友
1楼 · 发布于 2024-09-27 07:25:48

错误的原因很简单:

  • 第一个错误表明_服务是None,很明显它是None
  • 第二个错误是由签名引起的,根据python的说法,当使用@Slot(str)时,它表示它将只从QML接收一个参数,但在QML中传递3个参数

您似乎复制并粘贴了我以前的解决方案,但不了解它是如何工作的

my previous solution服务中是google日历实例,CalendarBackend是一个QObject,它允许在不阻塞Qt eventloop的情况下使用服务(这就是为什么使用线程),CalendarProvider只向QML公开一些方法

import functools
import logging
import os
import pickle
import sys
import threading

from PySide2 import QtCore, QtGui, QtQml

from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request


SCOPES = ["https://www.googleapis.com/auth/calendar"]
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


logging.basicConfig(level=logging.DEBUG)


def qdatetime_to_string(x):
    if isinstance(x, dict):
        for k, v in x.items():
            if isinstance(v, QtCore.QDateTime):
                x[k] = v.toString(QtCore.Qt.ISODate)
            else:
                qdatetime_to_string(v)
    elif isinstance(x, list):
        for i, e in enumerate(x):
            if isinstance(e, QtCore.QDateTime):
                x[i] = e.toString(QtCore.Qt.ISODate)
            else:
                qdatetime_to_string(v)


class Reply(QtCore.QObject):
    finished = QtCore.Signal()

    def __init__(self, func, args=(), kwargs=None, parent=None):
        super().__init__(parent)
        self._results = None
        self._is_finished = False
        self._error_str = ""
        threading.Thread(
            target=self._execute, args=(func, args, kwargs), daemon=True
        ).start()

    @property
    def results(self):
        return self._results

    @property
    def error_str(self):
        return self._error_str

    def is_finished(self):
        return self._is_finished

    def has_error(self):
        return bool(self._error_str)

    def _execute(self, func, args, kwargs):
        if kwargs is None:
            kwargs = {}
        try:
            self._results = func(*args, **kwargs)
        except Exception as e:
            self._error_str = str(e)
        self._is_finished = True
        self.finished.emit()


def convert_to_reply(func):
    def wrapper(*args, **kwargs):
        reply = Reply(func, args, kwargs)
        return reply

    return wrapper


class CalendarBackend(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._service = None

    @property
    def service(self):
        if self._service is None:
            reply = self._update_credentials()
            loop = QtCore.QEventLoop()
            reply.finished.connect(loop.quit)
            loop.exec_()
            if not reply.has_error():
                self._service = reply.results
            else:
                logging.debug(reply.error_str)
        return self._service

    @convert_to_reply
    def _update_credentials(self):
        creds = None
        if os.path.exists("token.pickle"):
            with open("token.pickle", "rb") as token:
                creds = pickle.load(token)
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    "credentials.json", SCOPES
                )
                creds = flow.run_local_server(port=0)
            with open("token.pickle", "wb") as token:
                pickle.dump(creds, token)
        return build("calendar", "v3", credentials=creds, cache_discovery=False)

    @convert_to_reply
    def insert(self, **kwargs):
        return self.service.events().insert(**kwargs).execute()


class CalendarProvider(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._backend = CalendarBackend()

    @QtCore.Slot("QVariant")
    def createEvent(self, parameters):
        kw = parameters.toVariant()
        if isinstance(kw, dict):
            qdatetime_to_string(kw)
            reply = self._backend.insert(**kw)
            wrapper = functools.partial(self.handle_finished_create_event, reply)
            reply.finished.connect(wrapper)

    def handle_finished_create_event(self, reply):
        if reply.has_error():
            logging.debug(reply.error_str)
        else:
            event = reply.results
            link = event.get("htmlLink", "")
            logging.debug("Event created: %s" % (link,))
            QtGui.QDesktopServices.openUrl(QtCore.QUrl(link))


if __name__ == "__main__":
    app = QtGui.QGuiApplication(sys.argv)

    QtQml.qmlRegisterType(CalendarProvider, "MyCalendar", 1, 0, "CalendarProvider")
    engine = QtQml.QQmlApplicationEngine()
    filename = os.path.join(CURRENT_DIR, "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(filename))

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())
import QtQuick 2.15
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.15

import MyCalendar 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 400
    minimumWidth: 400
    minimumHeight: 300
    color: "#f4f4f4"

    title: "Calendar Example"

    CalendarProvider {
        id: provider
    }
    GridLayout{
        anchors.centerIn: parent
        columns: 3
        TextField {
            id: eventstart
            placeholderText: qsTr("Start Time 01/12/2020 14:35:00")
            selectByMouse: true
        }
        TextField {
            id: eventend
            placeholderText: qsTr("End Time 01/12/2020 16:35:00")
            selectByMouse: true
        }
        TextField {
            id: eventinfo
            placeholderText: qsTr("Event Name")
            selectByMouse: true
        }
        Button{
            text: "Create Event"
            Layout.row: 1
            Layout.column: 1
            Layout.alignment: Qt.AlignHCenter
            onClicked: {
                var dt_start = Date.fromLocaleString(Qt.locale(), eventstart.text, "dd/MM/yyyy hh:mm:ss")
                var dt_end = Date.fromLocaleString(Qt.locale(), eventend.text, "dd/MM/yyyy hh:mm:ss")
                if(dt_start.getDate() && dt_end.getDate()){
                    provider.createEvent({
                        calendarId: "primary",
                        body: {
                            summary: eventinfo.text,
                            start: {
                                dateTime: dt_start,
                                timeZone: "America/Chicago",
                            },
                            end: {
                                dateTime: dt_end,
                                timeZone: "America/Chicago",
                            },
                        }
                    })
                }
            }
        }
    }
}

相关问题 更多 >

    热门问题