【问题标题】:Set system clock with QT on linux在 Linux 上使用 QT 设置系统时钟
【发布时间】:2018-05-04 11:15:47
【问题描述】:

我们将如何使用 QT 小部件应用程序以编程方式更改 linux 系统上的系统时间?

【问题讨论】:

    标签: linux qt


    【解决方案1】:

    您可以使用 dbus 与定时守护进程 https://www.freedesktop.org/wiki/Software/systemd/timedated/ 进行交互

    设置时间和日期。

    Qt 提供了一种从 xml 生成接口代码的方法 http://doc.qt.io/qt-5/qdbusxml2cpp.html。可以通过自省得到xml。

    我不喜欢生成的代码格式,所以我自己编写了界面代码

    h:

    #ifndef TIMEDATE1SERVICE_H
    #define TIMEDATE1SERVICE_H
    
    #include <QObject>
    #include <QString>
    #include <QVariant>
    #include <QtDBus>
    
    class Timedate1Interface: public QDBusAbstractInterface
    {
        Q_OBJECT
        Q_PROPERTY(bool CanNTP READ CanNTP)
        Q_PROPERTY(bool LocalRTC READ LocalRTC)
        Q_PROPERTY(bool NTP READ NTP)
        Q_PROPERTY(bool NTPSynchronized READ NTPSynchronized)
        Q_PROPERTY(qulonglong RTCTimeUSec READ RTCTimeUSec)
        Q_PROPERTY(qulonglong TimeUSec READ TimeUSec)
        Q_PROPERTY(QString Timezone READ Timezone)
    
    public:
        explicit Timedate1Interface(QObject *parent = nullptr);
    
        bool CanNTP() const;
        bool LocalRTC() const;
        bool NTP() const;
        bool NTPSynchronized() const;
        qulonglong RTCTimeUSec() const;
        qulonglong TimeUSec() const;
        QString Timezone() const;
    
        void SetLocalRTC(bool localRTC, bool fixSystem, bool userInteraction);
        void SetNTP(bool useNTP, bool userInteraction);
        void SetTime(qlonglong usecUTC, bool relative, bool userInteraction);
        void SetTimezone(const QString &timezone, bool userInteraction);
    };
    
    #endif // TIMEDATE1SERVICE_H
    

    cpp:

    #include "timedate1service.h"
    
    Timedate1Interface::Timedate1Interface(QObject *parent)
        : QDBusAbstractInterface("org.freedesktop.timedate1", "/org/freedesktop/timedate1",
                                 "org.freedesktop.timedate1", QDBusConnection::systemBus(), parent)
    {
    
    }
    
    bool Timedate1Interface::CanNTP() const
    {
        return qvariant_cast<bool>(property("CanNTP"));
    }
    
    bool Timedate1Interface::LocalRTC() const
    {
        return qvariant_cast<bool>(property("LocalRTC"));
    }
    
    bool Timedate1Interface::NTP() const
    {
        return qvariant_cast<bool>(property("NTP"));
    }
    
    bool Timedate1Interface::NTPSynchronized() const
    {
        return qvariant_cast<bool>(property("NTPSynchronized"));
    }
    
    qulonglong Timedate1Interface::RTCTimeUSec() const
    {
        return qvariant_cast<qulonglong>(property("RTCTimeUSec"));
    }
    
    qulonglong Timedate1Interface::TimeUSec() const
    {
        return qvariant_cast<qulonglong>(property("TimeUSec"));
    }
    
    QString Timedate1Interface::Timezone() const
    {
        return qvariant_cast<QString>(property("Timezone"));
    }
    
    void Timedate1Interface::SetLocalRTC(bool localRTC, bool fixSystem, bool userInteraction)
    {
        call("SetLocalRTC", localRTC, fixSystem, userInteraction);
    }
    
    void Timedate1Interface::SetNTP(bool useNTP, bool userInteraction)
    {
        call("SetNTP", useNTP, userInteraction);
    }
    
    void Timedate1Interface::SetTime(qlonglong usecUTC, bool relative, bool userInteraction)
    {
        call("SetTime", usecUTC, relative , userInteraction);
    }
    
    void Timedate1Interface::SetTimezone(const QString &timezone, bool userInteraction)
    {
        call("SetTimezone", timezone, userInteraction);
    }
    

    【讨论】:

    • 如果您有兴趣,我还可以提供一个包装器,用于与 qml 中的此类接口。
    • 感谢您的回答,并相信这将对其他人有用,但我不能使用 dbus 并且没有基于 systemd 的系统。使用 buildroot busybox init.d.
    【解决方案2】:

    你不能在纯 Qt 中做到这一点。您需要使用 Linux(或 POSIX)特定的东西。

    您可能不应该这样做,但最好将整个系统配置为使用NTP(例如,通过运行一些 NTP 客户端...)。大多数 Linux 发行版都已经有了。

    如果你真的想设置系统时间(但你不应该这样做 直接 Qt应用程序,因为 Qt 应用程序不应以 root 身份运行,但请参阅 this),请阅读 time(7) 然后 adjtimex(2) & settimeofday(2)

    您需要成为 root 用户,因此您不应该在 Qt 应用程序中这样做。您可以使用setuid 技术以root 身份运行某些特定命令或程序。 Setuid 很棘手(请参阅credentials(7)execve(2)setreuid(2)...),如果被滥用(并且容易出错),可能会打开一个巨大的 security hole,因此请阅读有关 Linux 编程的一些内容,例如旧的ALP

    因此,如果您坚持这样做(这可能是错误的),请为此用 C 语言编写一个小的特定程序,并将其设置为 setuid 并从您的 Qt 应用程序运行该 setuid 程序(例如,使用 QProcess)。

    【讨论】:

      【解决方案3】:

      我找到了一个简单的解决方案。由于我的系统非常简约,我不想使用 dbus 之类的东西。作为 root 或 sudoer,这可以执行(相当自我解释)-

      QString string = dateTime.toString("\"yyyy-MM-dd hh:mm\"");
      QString dateTimeString ("date -s ");
      dateTimeString.append(string);
      int systemDateTimeStatus= system(dateTimeString.toStdString().c_str());
      if (systemDateTimeStatus == -1)
      {
          qDebug() << "Failed to change date time";
      }
      int systemHwClockStatus = system("/sbin/hwclock -w");
      if (systemHwClockStatus == -1 )
      {
          qDebug() << "Failed to sync hardware clock";
      }
      

      【讨论】: