diff --git a/ksgrd/SensorShellAgent.h b/ksgrd/SensorShellAgent.h index 48b3db3..2bca44f 100644 --- a/ksgrd/SensorShellAgent.h +++ b/ksgrd/SensorShellAgent.h @@ -1,77 +1,77 @@ /* KSysGuard, the KDE System Guard Copyright (c) 1999, 2000 Chris Schlaeger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KSG_SENSORSHELLAGENT_H #define KSG_SENSORSHELLAGENT_H #include #include #include #include "SensorAgent.h" class QString; class KProcess; namespace KSGRD { class SensorManager; /** The SensorShellAgent starts a ksysguardd process and handles the asynchronous communication. It keeps a list of pending requests that have not been answered yet by ksysguard. The current implementation only allowes one pending requests. Incoming requests are queued in an input FIFO. */ class SensorShellAgent : public SensorAgent { Q_OBJECT public: explicit SensorShellAgent( SensorManager *sm ); ~SensorShellAgent(); bool start( const QString &host, const QString &shell, - const QString &command = QLatin1String(QLatin1String("")), int port = -1 ); + const QString &command = QLatin1String(QLatin1String("")), int port = -1 ) Q_DECL_OVERRIDE; - void hostInfo( QString &shell, QString &command, int &port) const; + void hostInfo( QString &shell, QString &command, int &port) const Q_DECL_OVERRIDE; private Q_SLOTS: void msgRcvd( ); void errMsgRcvd( ); void daemonExited( int exitCode, QProcess::ExitStatus exitStatus ); void daemonError( QProcess::ProcessError errorStatus ); private: - bool writeMsg( const char *msg, int len ); + bool writeMsg( const char *msg, int len ) Q_DECL_OVERRIDE; int mRetryCount; QPointer mDaemon; QString mShell; QString mCommand; }; } #endif diff --git a/ksgrd/SensorSocketAgent.h b/ksgrd/SensorSocketAgent.h index adaba1d..cddad47 100644 --- a/ksgrd/SensorSocketAgent.h +++ b/ksgrd/SensorSocketAgent.h @@ -1,71 +1,71 @@ /* KSysGuard, the KDE System Guard Copyright (c) 1999, 2000 Chris Schlaeger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KSG_SENSORSOCKETAGENT_H #define KSG_SENSORSOCKETAGENT_H #include #include "SensorAgent.h" class QString; namespace KSGRD { /** The SensorSocketAgent connects to a ksysguardd via a TCP connection. It keeps a list of pending requests that have not been answered yet by ksysguard. The current implementation only allowes one pending requests. Incoming requests are queued in an input FIFO. */ class SensorSocketAgent : public SensorAgent { Q_OBJECT public: explicit SensorSocketAgent( SensorManager *sm ); ~SensorSocketAgent(); bool start( const QString &host, const QString &shell, - const QString &command = QLatin1String(""), int port = -1 ); + const QString &command = QLatin1String(""), int port = -1 ) Q_DECL_OVERRIDE; - void hostInfo( QString &shell, QString &command, int &port ) const; + void hostInfo( QString &shell, QString &command, int &port ) const Q_DECL_OVERRIDE; private Q_SLOTS: void connectionClosed(); void msgSent(); void msgRcvd(); void error( QAbstractSocket::SocketError ); private: - bool writeMsg( const char *msg, int len ); + bool writeMsg( const char *msg, int len ) Q_DECL_OVERRIDE; QTcpSocket mSocket; int mPort; }; } #endif diff --git a/processcore/processes_atop_p.h b/processcore/processes_atop_p.h index b7f4a7e..fb6e8b6 100644 --- a/processcore/processes_atop_p.h +++ b/processcore/processes_atop_p.h @@ -1,79 +1,79 @@ /* This file is part of the KDE project Copyright (C) 2007 John Tapsell This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROCESSES_ATOP_H_ #define PROCESSES_ATOP_H_ #include "processes_base_p.h" #include //For sysconf #include class QDateTime; namespace KSysGuard { class Process; /** * This is the ATOP specific code to get process information for the local host. */ class ProcessesATop : public AbstractProcesses { public: explicit ProcessesATop(bool loadDefaultFile = true); virtual ~ProcessesATop(); - virtual QSet getAllPids(); - virtual long getParentPid(long pid); - virtual bool updateProcessInfo(long pid, Process *process); - virtual bool sendSignal(long pid, int sig); - virtual bool setNiceness(long pid, int priority); - virtual bool setScheduler(long pid, int priorityClass, int priority); - virtual long long totalPhysicalMemory(); - virtual bool setIoNiceness(long pid, int priorityClass, int priority); - virtual bool supportsIoNiceness(); - virtual long numberProcessorCores() + QSet getAllPids() Q_DECL_OVERRIDE; + long getParentPid(long pid) Q_DECL_OVERRIDE; + bool updateProcessInfo(long pid, Process *process) Q_DECL_OVERRIDE; + bool sendSignal(long pid, int sig) Q_DECL_OVERRIDE; + bool setNiceness(long pid, int priority) Q_DECL_OVERRIDE; + bool setScheduler(long pid, int priorityClass, int priority) Q_DECL_OVERRIDE; + long long totalPhysicalMemory() Q_DECL_OVERRIDE; + bool setIoNiceness(long pid, int priorityClass, int priority) Q_DECL_OVERRIDE; + bool supportsIoNiceness() Q_DECL_OVERRIDE; + long numberProcessorCores() Q_DECL_OVERRIDE #ifdef _SC_NPROCESSORS_ONLN { return sysconf(_SC_NPROCESSORS_ONLN); } // Should work on any recent posix system #else ; #endif - virtual void updateAllProcesses(Processes::UpdateFlags updateFlags) { mUpdateFlags = updateFlags; emit processesUpdated(); } //For local machine, there is no delay + void updateAllProcesses(Processes::UpdateFlags updateFlags) Q_DECL_OVERRIDE { mUpdateFlags = updateFlags; emit processesUpdated(); } //For local machine, there is no delay bool isHistoryAvailable() const; QDateTime viewingTime() const; bool setViewingTime(const QDateTime &when); QList< QPair > historiesAvailable() const; bool loadHistoryFile(const QString &filename); QString historyFileName() const; private: /** * You can use this for whatever data you want. * Be careful about preserving state in between getParentPid and updateProcessInfo calls * if you chose to do that. getParentPid may be called several times * for different pids before the relevant updateProcessInfo calls are made. * This is because the tree structure has to be sorted out first. */ class Private; Private *d; Processes::UpdateFlags mUpdateFlags; }; } #endif diff --git a/processcore/processes_local_p.h b/processcore/processes_local_p.h index 75cf9f5..eee8da8 100644 --- a/processcore/processes_local_p.h +++ b/processcore/processes_local_p.h @@ -1,72 +1,72 @@ /* This file is part of the KDE project Copyright (C) 2007 John Tapsell This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROCESSES_LOCAL_H_ #define PROCESSES_LOCAL_H_ #include "processes_base_p.h" #include //For sysconf #include namespace KSysGuard { class Process; /** * This is the OS specific code to get process information for the local host. */ class ProcessesLocal : public AbstractProcesses { public: ProcessesLocal(); virtual ~ProcessesLocal(); - virtual QSet getAllPids(); - virtual long getParentPid(long pid); - virtual bool updateProcessInfo(long pid, Process *process); - virtual bool sendSignal(long pid, int sig); - virtual bool setNiceness(long pid, int priority); - virtual bool setScheduler(long pid, int priorityClass, int priority); - virtual long long totalPhysicalMemory(); - virtual bool setIoNiceness(long pid, int priorityClass, int priority); - virtual bool supportsIoNiceness(); - virtual long numberProcessorCores() + QSet getAllPids() Q_DECL_OVERRIDE; + long getParentPid(long pid) Q_DECL_OVERRIDE; + bool updateProcessInfo(long pid, Process *process) Q_DECL_OVERRIDE; + bool sendSignal(long pid, int sig) Q_DECL_OVERRIDE; + bool setNiceness(long pid, int priority) Q_DECL_OVERRIDE; + bool setScheduler(long pid, int priorityClass, int priority) Q_DECL_OVERRIDE; + long long totalPhysicalMemory() Q_DECL_OVERRIDE; + bool setIoNiceness(long pid, int priorityClass, int priority) Q_DECL_OVERRIDE; + bool supportsIoNiceness() Q_DECL_OVERRIDE; + long numberProcessorCores() Q_DECL_OVERRIDE #ifdef _SC_NPROCESSORS_ONLN { return sysconf(_SC_NPROCESSORS_ONLN); } // Should work on any recent posix system #else ; #endif - virtual void updateAllProcesses(Processes::UpdateFlags updateFlags) { mUpdateFlags = updateFlags; emit processesUpdated(); } //For local machine, there is no delay + void updateAllProcesses(Processes::UpdateFlags updateFlags) Q_DECL_OVERRIDE { mUpdateFlags = updateFlags; emit processesUpdated(); } //For local machine, there is no delay private: /** * You can use this for whatever data you want. * Be careful about preserving state in between getParentPid and updateProcessInfo calls * if you chose to do that. getParentPid may be called several times * for different pids before the relevant updateProcessInfo calls are made. * This is because the tree structure has to be sorted out first. */ class Private; Private *d; Processes::UpdateFlags mUpdateFlags; }; } #endif diff --git a/processcore/processes_remote_p.h b/processcore/processes_remote_p.h index 795f56b..6b883a0 100644 --- a/processcore/processes_remote_p.h +++ b/processcore/processes_remote_p.h @@ -1,78 +1,78 @@ /* This file is part of the KDE project Copyright (C) 2007 John Tapsell This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROCESSES_REMOTE_P_H_ #define PROCESSES_REMOTE_P_H_ #include "processes_base_p.h" #include class Process; namespace KSysGuard { /** * This is used to connect to a remote host */ class ProcessesRemote : public AbstractProcesses { Q_OBJECT public: ProcessesRemote(const QString &hostname); virtual ~ProcessesRemote(); - virtual QSet getAllPids(); - virtual long getParentPid(long pid); - virtual bool updateProcessInfo(long pid, Process *process); - virtual bool sendSignal(long pid, int sig); - virtual bool setNiceness(long pid, int priority); - virtual bool setScheduler(long pid, int priorityClass, int priority); - virtual long long totalPhysicalMemory(); - virtual bool setIoNiceness(long pid, int priorityClass, int priority); - virtual bool supportsIoNiceness(); - virtual long numberProcessorCores(); - virtual void updateAllProcesses( Processes::UpdateFlags updateFlags ); + QSet getAllPids() Q_DECL_OVERRIDE; + long getParentPid(long pid) Q_DECL_OVERRIDE; + bool updateProcessInfo(long pid, Process *process) Q_DECL_OVERRIDE; + bool sendSignal(long pid, int sig) Q_DECL_OVERRIDE; + bool setNiceness(long pid, int priority) Q_DECL_OVERRIDE; + bool setScheduler(long pid, int priorityClass, int priority) Q_DECL_OVERRIDE; + long long totalPhysicalMemory() Q_DECL_OVERRIDE; + bool setIoNiceness(long pid, int priorityClass, int priority) Q_DECL_OVERRIDE; + bool supportsIoNiceness() Q_DECL_OVERRIDE; + long numberProcessorCores() Q_DECL_OVERRIDE; + void updateAllProcesses( Processes::UpdateFlags updateFlags ) Q_DECL_OVERRIDE; Q_SIGNALS: /** For a remote machine, we rely on being able to communicate with ksysguardd. * This must be dealt with by the program including this widget. It must listen to our * 'runCommand' signal, and run the given command, with the given id. */ void runCommand(const QString &command, int id); public Q_SLOTS: /** For a remote machine, we rely on being able to communicate with ksysguardd. * The programming using this must call this slot when an answer is received from ksysguardd, * in response to a runCommand request. The id identifies the answer */ void answerReceived( int id, const QList& answer ); /** Called soon after */ void setup(); protected: enum { PsInfo, Ps, UsedMemory, FreeMemory, Kill, Renice, Ionice }; private: /** * You can use this for whatever data you want. Be careful about preserving state in between getParentPid and updateProcessInfo calls * if you chose to do that. getParentPid may be called several times for different pids before the relevant updateProcessInfo calls are made. * This is because the tree structure has to be sorted out first. */ class Private; Private *d; }; } #endif diff --git a/processui/ProcessFilter.h b/processui/ProcessFilter.h index 28a5abd..055b0ca 100644 --- a/processui/ProcessFilter.h +++ b/processui/ProcessFilter.h @@ -1,63 +1,63 @@ /* KSysGuard, the KDE System Guard Copyright (c) 1999, 2000 Chris Schlaeger Copyright (c) 2006 John Tapsell This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROCESSFILTER_H_ #define PROCESSFILTER_H_ #include #include class QModelIndex; #ifdef Q_OS_WIN // this workaround is needed to make krunner link under msvc // please keep it this way even if you port this library to have a _export.h header file #define KSYSGUARD_EXPORT #else #define KSYSGUARD_EXPORT Q_DECL_EXPORT #endif class KSYSGUARD_EXPORT ProcessFilter : public QSortFilterProxyModel { Q_OBJECT Q_ENUMS(State) public: enum State {AllProcesses=0,AllProcessesInTreeForm, SystemProcesses, UserProcesses, OwnProcesses, ProgramsOnly}; ProcessFilter(QObject *parent=0) : QSortFilterProxyModel(parent) {mFilter = AllProcesses;} virtual ~ProcessFilter() {} - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; State filter() const {return mFilter; } public Q_SLOTS: void setFilter(State index); protected: - virtual bool filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const; + bool filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const Q_DECL_OVERRIDE; State mFilter; }; #endif diff --git a/processui/ProcessModel.h b/processui/ProcessModel.h index 7e2788c..6d64b80 100644 --- a/processui/ProcessModel.h +++ b/processui/ProcessModel.h @@ -1,205 +1,205 @@ /* KSysGuard, the KDE System Guard Copyright (c) 1999, 2000 Chris Schlaeger Copyright (c) 2006 John Tapsell This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PROCESSMODEL_H_ #define PROCESSMODEL_H_ #include #include namespace KSysGuard { class Processes; class Process; } class ProcessModelPrivate; #ifdef Q_OS_WIN // this workaround is needed to make krunner link under msvc // please keep it this way even if you port this library to have a _export.h header file #define KSYSGUARD_EXPORT #else #define KSYSGUARD_EXPORT Q_DECL_EXPORT #endif class KSYSGUARD_EXPORT ProcessModel : public QAbstractItemModel { Q_OBJECT Q_ENUMS(Units) public: ProcessModel(QObject* parent = 0, const QString &host = QString() ); virtual ~ProcessModel(); /* Functions for our Model for QAbstractItemModel*/ - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount ( const QModelIndex & parent = QModelIndex() ) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; - QModelIndex parent ( const QModelIndex & index ) const; - - bool hasChildren ( const QModelIndex & parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int columnCount ( const QModelIndex & parent = QModelIndex() ) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const Q_DECL_OVERRIDE; + QModelIndex parent ( const QModelIndex & index ) const Q_DECL_OVERRIDE; + + bool hasChildren ( const QModelIndex & parent) const Q_DECL_OVERRIDE; /** Returns if (left < right), used by the sort-filter proxy model to sort the columns */ bool lessThan( const QModelIndex & left, const QModelIndex & right) const; /* Functions for drag and drop and copying to clipboard, inherited from QAbstractItemModel */ - QStringList mimeTypes() const; - QMimeData *mimeData(const QModelIndexList &indexes) const; - Qt::ItemFlags flags(const QModelIndex &index) const; + QStringList mimeTypes() const Q_DECL_OVERRIDE; + QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; /* Functions for setting the model */ /** Setup the column headings by inserting the appropriate headings into the model. * Can be called more than once to retranslate the headings if the system language changes. */ void setupHeader(); /** Update data. You can pass in the time between updates to only update if there hasn't * been an update within the last @p updateDurationMSecs milliseconds. 0 indicate to update * regardless of when the last update was. * The updateFlags indicates what to additional update, as well as the usual details. */ void update(long updateDurationMSecs = 0, KSysGuard::Processes::UpdateFlags updateFlags = KSysGuard::Processes::IOStatistics); /** Return a string with the pid of the process and the name of the process. E.g. 13343: ksysguard */ QString getStringForProcess(KSysGuard::Process *process) const; KSysGuard::Process *getProcess(qlonglong pid); /** This is used from ProcessFilter to get the process at a given index when in flat mode */ KSysGuard::Process *getProcessAtIndex(int index) const; /** Returns whether this user can log in or not. * @see mUidCanLogin */ bool canUserLogin(long uid) const; /** In simple mode, everything is flat, with no icons, few if any colors, no xres etc. * This can be changed at any time. It is a fairly quick operation. Basically it resets the model */ void setSimpleMode(bool simple); /** In simple mode, everything is flat, with no icons, few if any colors, no xres etc */ bool isSimpleMode() const; /** Returns the total amount of physical memory in the machine. */ qlonglong totalMemory() const; /** This returns a QModelIndex for the given process. It has to look up the parent for this pid, find the offset this * pid is from the parent, and return that. It's not that slow, but does involve a couple of hash table lookups. */ QModelIndex getQModelIndex ( KSysGuard::Process *process, int column) const; /** Whether this is showing the processes for the current machine */ bool isLocalhost() const; /** The host name that this widget is showing the processes of */ QString hostName() const; /** Whether this process has a GUI window */ bool hasGUIWindow(qlonglong pid) const; /** Returns for process controller pointer for this model */ KSysGuard::Processes *processController() const; //The processes instance /** Convenience function to get the number of processes. * * Equivalent to processController->processCount() */ int processCount() const { return processController()->processCount(); } /** The headings in the model. The order here is the order that they are shown * in. If you change this, make sure you also change the * setup header function, and make sure you increase PROCESSHEADERVERSION. This will ensure * that old saved settings won't be used */ #define PROCESSHEADERVERSION 6 enum { HeadingName=0, HeadingUser, HeadingPid, HeadingTty, HeadingNiceness, HeadingCPUUsage, HeadingCPUTime, HeadingIoRead, HeadingIoWrite, HeadingVmSize, HeadingMemory, HeadingSharedMemory, HeadingStartTime, HeadingCommand, HeadingXMemory, HeadingXTitle }; enum { UidRole = Qt::UserRole, SortingValueRole, WindowIdRole, PlainValueRole, PercentageRole }; bool showTotals() const; /** When displaying memory sizes, this is the units it should be displayed in */ enum Units { UnitsAuto, UnitsKB, UnitsMB, UnitsGB, UnitsTB, UnitsPB, UnitsPercentage }; /** Set the units memory sizes etc should be displayed in */ void setUnits(Units units); /** The units memory sizes etc should be displayed in */ Units units() const; /** Set the I/O units sizes etc should be displayed in */ void setIoUnits(Units units); /** The units I/O sizes etc should be displayed in */ Units ioUnits() const; enum IoInformation { Bytes, Syscalls, ActualBytes, BytesRate, SyscallsRate, ActualBytesRate }; /** Set the information to show in the Io Read and Io Write columns */ void setIoInformation( IoInformation ioInformation ); /** The information to show in the Io Read and Io Write columns */ IoInformation ioInformation() const; /** Take an amount in kb, and return a string in the units set by setUnits() */ QString formatMemoryInfo(qlonglong amountInKB, Units units, bool returnEmptyIfValueIsZero = false) const; /** Whether to show the command line options in the process name column */ bool isShowCommandLineOptions() const; /** Set whether to show the command line options in the process name column */ void setShowCommandLineOptions(bool showCommandLineOptions); /** Whether to show tooltips when the mouse hovers over a process */ bool isShowingTooltips() const; /** Set whether to show tooltips when the mouse hovers over a process */ void setShowingTooltips(bool showTooltips); /** Whether to divide CPU usage by the number of CPUs */ bool isNormalizedCPUUsage() const; /** Set whether to divide CPU usage by the number of CPUs */ void setNormalizedCPUUsage(bool normalizeCPUUsage); /** Retranslate the GUI, for when the system language changes */ void retranslateUi(); public Q_SLOTS: /** Whether to show the total cpu for the process plus all of its children */ void setShowTotals(bool showTotals); private: ProcessModelPrivate* const d; friend class ProcessModelPrivate; }; #endif diff --git a/processui/ksysguardprocesslist.cpp b/processui/ksysguardprocesslist.cpp index dac6f9a..79e9bb3 100644 --- a/processui/ksysguardprocesslist.cpp +++ b/processui/ksysguardprocesslist.cpp @@ -1,1473 +1,1473 @@ /* KSysGuard, the KDE System Guard Copyright (c) 1999 - 2001 Chris Schlaeger Copyright (c) 2006 - 2007 John Tapsell This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ksysguardprocesslist.h" #include "../config-ksysguard.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //For SIGTERM #include #include #include #include #include #include #include #include "ReniceDlg.h" #include "ui_ProcessWidgetUI.h" #include "scripting.h" #include #include //Trolltech have a testing class for classes that inherit QAbstractItemModel. If you want to run with this run-time testing enabled, put the modeltest.* files in this directory and uncomment the next line //#define DO_MODELCHECK #ifdef DO_MODELCHECK #include "modeltest.h" #endif class ProgressBarItemDelegate : public QStyledItemDelegate { public: ProgressBarItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { } - virtual void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const + void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const Q_DECL_OVERRIDE { QStyleOptionViewItemV4 option = opt; initStyleOption(&option,index); float percentage = index.data(ProcessModel::PercentageRole).toFloat(); if (percentage >= 0) drawPercentageDisplay(painter,option, percentage); else QStyledItemDelegate::paint(painter, option, index); } private: inline void drawPercentageDisplay(QPainter *painter, QStyleOptionViewItemV4 &option, float percentage) const { QStyle *style = option.widget ? option.widget->style() : QApplication::style(); // draw the background style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget); QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) cg = QPalette::Inactive; //Now draw our percentage thingy const QRect &rect = option.rect; int size = qMin(percentage,1.0f) * rect.width(); if(size > 2 ) { //make sure the line will have a width of more than 1 pixel painter->setPen(Qt::NoPen); QColor color = option.palette.color(cg, QPalette::Link); color.setAlpha(50); painter->fillRect( rect.x(), rect.y(), size, rect.height(), color); } // draw the text if (!option.text.isEmpty()) { QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &option, option.widget); if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(option.palette.color(cg, QPalette::Text)); } painter->setFont(option.font); QTextOption textOption; textOption.setWrapMode(QTextOption::ManualWrap); textOption.setTextDirection(option.direction); textOption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment)); painter->drawText(textRect, option.text, textOption); } // draw the focus rect if (option.state & QStyle::State_HasFocus) { QStyleOptionFocusRect o; o.QStyleOption::operator=(option); o.rect = style->subElementRect(QStyle::SE_ItemViewItemFocusRect, &option, option.widget); o.state |= QStyle::State_KeyboardFocusChange; o.state |= QStyle::State_Item; QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, option.widget); } } }; struct KSysGuardProcessListPrivate { KSysGuardProcessListPrivate(KSysGuardProcessList* q, const QString &hostName) : mModel(q, hostName), mFilterModel(q), mUi(new Ui::ProcessWidget()), mProcessContextMenu(NULL), mUpdateTimer(NULL) { mScripting = NULL; mNeedToExpandInit = false; mNumItemsSelected = -1; mResortCountDown = 2; //The items added initially will be already sorted, but without CPU info. On the second refresh we will have CPU usage, so /then/ we can resort renice = new QAction(i18np("Set Priority...", "Set Priority...", 1), q); renice->setShortcut(Qt::Key_F8); selectParent = new QAction(i18n("Jump to Parent Process"), q); selectTracer = new QAction(i18n("Jump to Process Debugging This One"), q); window = new QAction(i18n("Show Application Window"), q); resume = new QAction(i18n("Resume Stopped Process"), q); terminate = new QAction(i18np("End Process", "End Processes", 1), q); terminate->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); terminate->setShortcut(Qt::Key_Delete); kill = new QAction(i18np("Forcibly Kill Process", "Forcibly Kill Processes", 1), q); kill->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); kill->setShortcut(Qt::SHIFT + Qt::Key_Delete); sigStop = new QAction(i18n("Suspend (STOP)"), q); sigCont = new QAction(i18n("Continue (CONT)"), q); sigHup = new QAction(i18n("Hangup (HUP)"), q); sigInt = new QAction(i18n("Interrupt (INT)"), q); sigTerm = new QAction(i18n("Terminate (TERM)"), q); sigKill = new QAction(i18n("Kill (KILL)"), q); sigUsr1 = new QAction(i18n("User 1 (USR1)"), q); sigUsr2 = new QAction(i18n("User 2 (USR2)"), q); //Set up '/' as a shortcut to jump to the quick search text widget jumpToSearchFilter = new QAction(i18n("Focus on Quick Search"), q); jumpToSearchFilter->setShortcuts(QList() << QKeySequence::Find << '/'); } ~KSysGuardProcessListPrivate() { delete mUi; mUi = NULL; } /** The number rows and their children for the given parent in the mFilterModel model */ int totalRowCount(const QModelIndex &parent) const; /** Helper function to setup 'action' with the given pids */ void setupKAuthAction(KAuth::Action &action, const QList & pids) const; /** fire a timer event if we are set to use our internal timer*/ void fireTimerEvent(); /** The process model. This contains all the data on all the processes running on the system */ ProcessModel mModel; /** The process filter. The mModel is connected to this, and this filter model connects to the view. This lets us * sort the view and filter (by using the combo box or the search line) */ ProcessFilter mFilterModel; /** The graphical user interface for this process list widget, auto-generated by Qt Designer */ Ui::ProcessWidget *mUi; /** The context menu when you right click on a process */ QMenu *mProcessContextMenu; /** A timer to call updateList() every mUpdateIntervalMSecs. * NULL is mUpdateIntervalMSecs is <= 0. */ QTimer *mUpdateTimer; /** The time to wait, in milliseconds, between updating the process list */ int mUpdateIntervalMSecs; /** Number of items that are selected */ int mNumItemsSelected; /** Class to deal with the scripting. NULL if scripting is disabled */ Scripting *mScripting; /** A counter to mark when to resort, so that we do not resort on every update */ int mResortCountDown; bool mNeedToExpandInit; QAction *renice; QAction *terminate; QAction *kill; QAction *selectParent; QAction *selectTracer; QAction *jumpToSearchFilter; QAction *window; QAction *resume; QAction *sigStop; QAction *sigCont; QAction *sigHup; QAction *sigInt; QAction *sigTerm; QAction *sigKill; QAction *sigUsr1; QAction *sigUsr2; }; KSysGuardProcessList::KSysGuardProcessList(QWidget* parent, const QString &hostName) : QWidget(parent), d(new KSysGuardProcessListPrivate(this, hostName)) { qRegisterMetaType >(); qDBusRegisterMetaType >(); d->mUpdateIntervalMSecs = 0; //Set process to not update manually by default d->mUi->setupUi(this); d->mFilterModel.setSourceModel(&d->mModel); d->mUi->treeView->setModel(&d->mFilterModel); #ifdef DO_MODELCHECK new ModelTest(&d->mModel, this); #endif d->mUi->treeView->setItemDelegate(new ProgressBarItemDelegate(d->mUi->treeView)); d->mUi->treeView->header()->setContextMenuPolicy(Qt::CustomContextMenu); connect(d->mUi->treeView->header(), &QWidget::customContextMenuRequested, this, &KSysGuardProcessList::showColumnContextMenu); d->mProcessContextMenu = new QMenu(d->mUi->treeView); d->mUi->treeView->setContextMenuPolicy(Qt::CustomContextMenu); connect(d->mUi->treeView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showProcessContextMenu(QPoint))); d->mUi->treeView->header()->setSectionsClickable(true); d->mUi->treeView->header()->setSortIndicatorShown(true); d->mUi->treeView->header()->setCascadingSectionResizes(false); connect(d->mUi->btnKillProcess, &QAbstractButton::clicked, this, &KSysGuardProcessList::killSelectedProcesses); connect(d->mUi->txtFilter, &QLineEdit::textChanged, this, &KSysGuardProcessList::filterTextChanged); connect(d->mUi->cmbFilter, SIGNAL(currentIndexChanged(int)), this, SLOT(setStateInt(int))); connect(d->mUi->treeView, &QTreeView::expanded, this, &KSysGuardProcessList::expandAllChildren); connect(d->mUi->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KSysGuardProcessList::selectionChanged); connect(&d->mFilterModel, &QAbstractItemModel::rowsInserted, this, &KSysGuardProcessList::rowsInserted); connect(&d->mFilterModel, &QAbstractItemModel::rowsRemoved, this, &KSysGuardProcessList::processListChanged); setMinimumSize(sizeHint()); d->mFilterModel.setFilterKeyColumn(-1); /* Hide various columns by default, to reduce information overload */ d->mUi->treeView->header()->hideSection(ProcessModel::HeadingVmSize); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingNiceness); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingTty); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingStartTime); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingCommand); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingPid); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingCPUTime); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingIoRead); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingIoWrite); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingXMemory); // NOTE! After this is all setup, the settings for the header are restored // from the user's last run. (in restoreHeaderState) // So making changes here only affects the default settings. To // test changes temporarily, comment out the lines in restoreHeaderState. // When you are happy with the changes and want to commit, increase the // value of PROCESSHEADERVERSION. This will force the header state // to be reset back to the defaults for all users. d->mUi->treeView->header()->resizeSection(ProcessModel::HeadingCPUUsage, d->mUi->treeView->header()->sectionSizeHint(ProcessModel::HeadingCPUUsage)); d->mUi->treeView->header()->resizeSection(ProcessModel::HeadingMemory, d->mUi->treeView->header()->sectionSizeHint(ProcessModel::HeadingMemory)); d->mUi->treeView->header()->resizeSection(ProcessModel::HeadingSharedMemory, d->mUi->treeView->header()->sectionSizeHint(ProcessModel::HeadingSharedMemory)); d->mUi->treeView->header()->setSectionResizeMode(0, QHeaderView::Interactive); d->mUi->treeView->header()->setStretchLastSection(true); //Process names can have mixed case. Make the filter case insensitive. d->mFilterModel.setFilterCaseSensitivity(Qt::CaseInsensitive); d->mFilterModel.setSortCaseSensitivity(Qt::CaseInsensitive); d->mUi->txtFilter->installEventFilter(this); d->mUi->treeView->installEventFilter(this); d->mUi->treeView->setDragEnabled(true); d->mUi->treeView->setDragDropMode(QAbstractItemView::DragOnly); //Sort by username by default d->mUi->treeView->sortByColumn(ProcessModel::HeadingUser, Qt::AscendingOrder); // Add all the actions to the main widget, and get all the actions to call actionTriggered when clicked QSignalMapper *signalMapper = new QSignalMapper(this); QList actions; actions << d->renice << d->kill << d->terminate << d->selectParent << d->selectTracer << d->window << d->jumpToSearchFilter; actions << d->resume << d->sigStop << d->sigCont << d->sigHup << d->sigInt << d->sigTerm << d->sigKill << d->sigUsr1 << d->sigUsr2; foreach(QAction *action, actions) { addAction(action); connect(action, SIGNAL(triggered(bool)), signalMapper, SLOT(map())); signalMapper->setMapping(action, action); } connect(signalMapper, SIGNAL(mapped(QObject*)), SLOT(actionTriggered(QObject*))); retranslateUi(); d->mUi->btnKillProcess->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); d->mUi->btnKillProcess->setToolTip(i18n("End the selected process. Warning - you may lose unsaved work.
Right click on a process to send other signals.
See What's This for technical information.
To target a specific window to kill, press Ctrl+Alt+Esc at any time.")); } KSysGuardProcessList::~KSysGuardProcessList() { delete d; } QTreeView *KSysGuardProcessList::treeView() const { return d->mUi->treeView; } QLineEdit *KSysGuardProcessList::filterLineEdit() const { return d->mUi->txtFilter; } ProcessFilter::State KSysGuardProcessList::state() const { return d->mFilterModel.filter(); } void KSysGuardProcessList::setStateInt(int state) { setState((ProcessFilter::State) state); d->mUi->treeView->scrollTo( d->mUi->treeView->currentIndex()); } void KSysGuardProcessList::setState(ProcessFilter::State state) { //index is the item the user selected in the combo box d->mFilterModel.setFilter(state); d->mModel.setSimpleMode( (state != ProcessFilter::AllProcessesInTreeForm) ); d->mUi->cmbFilter->setCurrentIndex( (int)state); if(isVisible()) expandInit(); } void KSysGuardProcessList::filterTextChanged(const QString &newText) { d->mFilterModel.setFilterRegExp(newText.trimmed()); if(isVisible()) expandInit(); d->mUi->btnKillProcess->setEnabled( d->mUi->treeView->selectionModel()->hasSelection() ); d->mUi->treeView->scrollTo( d->mUi->treeView->currentIndex()); } int KSysGuardProcessList::visibleProcessesCount() const { //This assumes that all the visible rows are processes. This is true currently, but might not be //true if we add support for showing threads etc if(d->mModel.isSimpleMode()) return d->mFilterModel.rowCount(); return d->totalRowCount(QModelIndex()); } int KSysGuardProcessListPrivate::totalRowCount(const QModelIndex &parent ) const { int numRows = mFilterModel.rowCount(parent); int total = numRows; for (int i = 0; i < numRows; ++i) { QModelIndex index = mFilterModel.index(i, 0,parent); //if it has children add the total if (mFilterModel.hasChildren(index)) total += totalRowCount(index); } return total; } void KSysGuardProcessListPrivate::setupKAuthAction(KAuth::Action &action, const QList & pids) const { action.setHelperId(QStringLiteral("org.kde.ksysguard.processlisthelper")); int processCount = pids.count(); for(int i = 0; i < processCount; i++) { action.addArgument(QStringLiteral("pid%1").arg(i), pids[i]); } action.addArgument(QStringLiteral("pidcount"), processCount); } void KSysGuardProcessList::selectionChanged() { int numSelected = d->mUi->treeView->selectionModel()->selectedRows().size(); if(numSelected == d->mNumItemsSelected) return; d->mNumItemsSelected = numSelected; d->mUi->btnKillProcess->setEnabled( numSelected != 0 ); d->renice->setText(i18np("Set Priority...", "Set Priority...", numSelected)); d->kill->setText(i18np("Forcibly Kill Process", "Forcibly Kill Processes", numSelected)); d->terminate->setText(i18ncp("Context menu", "End Process", "End Processes", numSelected)); } void KSysGuardProcessList::showProcessContextMenu(const QModelIndex &index) { if(!index.isValid()) return; QRect rect = d->mUi->treeView->visualRect(index); QPoint point(rect.x() + rect.width()/4, rect.y() + rect.height()/2 ); showProcessContextMenu(point); } void KSysGuardProcessList::showProcessContextMenu(const QPoint &point) { d->mProcessContextMenu->clear(); QModelIndexList selectedIndexes = d->mUi->treeView->selectionModel()->selectedRows(); int numProcesses = selectedIndexes.size(); if(numProcesses == 0) { //No processes selected, so no process context menu //Check just incase we have no columns visible. In which case show the column context menu //so that users can unhide columns if there are no columns visible for(int i = 0; i < d->mFilterModel.columnCount(); ++i) { if(!d->mUi->treeView->header()->isSectionHidden(i)) return; } showColumnContextMenu(point); return; } QModelIndex realIndex = d->mFilterModel.mapToSource(selectedIndexes.at(0)); KSysGuard::Process *process = reinterpret_cast (realIndex.internalPointer()); //If the selected process is a zombie, do not bother offering renice and kill options bool showSignalingEntries = numProcesses != 1 || process->status() != KSysGuard::Process::Zombie; if(showSignalingEntries) { d->mProcessContextMenu->addAction(d->renice); QMenu *signalMenu = d->mProcessContextMenu->addMenu(i18n("Send Signal")); signalMenu->addAction(d->sigStop); signalMenu->addAction(d->sigCont); signalMenu->addAction(d->sigHup); signalMenu->addAction(d->sigInt); signalMenu->addAction(d->sigTerm); signalMenu->addAction(d->sigKill); signalMenu->addAction(d->sigUsr1); signalMenu->addAction(d->sigUsr2); } if(numProcesses == 1 && process->parentPid() > 1) { //As a design decision, I do not show the 'Jump to parent process' option when the //parent is just 'init'. KSysGuard::Process *parent_process = d->mModel.getProcess(process->parentPid()); if(parent_process) { //it should not be possible for this process to not exist, but check just incase QString parent_name = parent_process->name(); d->selectParent->setText(i18n("Jump to Parent Process (%1)", parent_name)); d->mProcessContextMenu->addAction(d->selectParent); } } if(numProcesses == 1 && process->tracerpid() >= 0) { //If the process is being debugged, offer to select it d->mProcessContextMenu->addAction(d->selectTracer); } if (numProcesses == 1 && !d->mModel.data(realIndex, ProcessModel::WindowIdRole).isNull()) { d->mProcessContextMenu->addAction(d->window); } if(numProcesses == 1 && process->status() == KSysGuard::Process::Stopped) { //If the process is stopped, offer to resume it d->mProcessContextMenu->addAction(d->resume); } if(numProcesses == 1 && d->mScripting) { foreach(QAction *action, d->mScripting->actions()) { d->mProcessContextMenu->addAction(action); } } if (showSignalingEntries) { d->mProcessContextMenu->addSeparator(); d->mProcessContextMenu->addAction(d->terminate); if (numProcesses == 1 && !process->timeKillWasSent().isNull()) d->mProcessContextMenu->addAction(d->kill); } d->mProcessContextMenu->popup(d->mUi->treeView->viewport()->mapToGlobal(point)); } void KSysGuardProcessList::actionTriggered(QObject *object) { if(!isVisible()) //Ignore triggered actions if we are not visible! return; //Reset the text back to normal d->selectParent->setText(i18n("Jump to Parent Process")); QAction *result = qobject_cast(object); if(result == 0) { //Escape was pressed. Do nothing. } else if(result == d->renice) { reniceSelectedProcesses(); } else if(result == d->terminate) { sendSignalToSelectedProcesses(SIGTERM, true); } else if(result == d->kill) { sendSignalToSelectedProcesses(SIGKILL, true); } else if(result == d->selectParent) { QModelIndexList selectedIndexes = d->mUi->treeView->selectionModel()->selectedRows(); int numProcesses = selectedIndexes.size(); if(numProcesses == 0) return; //No processes selected QModelIndex realIndex = d->mFilterModel.mapToSource(selectedIndexes.at(0)); KSysGuard::Process *process = reinterpret_cast (realIndex.internalPointer()); if(process) selectAndJumpToProcess(process->parentPid()); } else if(result == d->selectTracer) { QModelIndexList selectedIndexes = d->mUi->treeView->selectionModel()->selectedRows(); int numProcesses = selectedIndexes.size(); if(numProcesses == 0) return; //No processes selected QModelIndex realIndex = d->mFilterModel.mapToSource(selectedIndexes.at(0)); KSysGuard::Process *process = reinterpret_cast (realIndex.internalPointer()); if(process) selectAndJumpToProcess(process->tracerpid()); } else if(result == d->window) { QModelIndexList selectedIndexes = d->mUi->treeView->selectionModel()->selectedRows(); int numProcesses = selectedIndexes.size(); if(numProcesses == 0) return; //No processes selected foreach( const QModelIndex &index, selectedIndexes) { QModelIndex realIndex = d->mFilterModel.mapToSource(index); QVariant widVar= d->mModel.data(realIndex, ProcessModel::WindowIdRole); if( !widVar.isNull() ) { int wid = widVar.toInt(); KWindowSystem::activateWindow(wid); } } } else if(result == d->jumpToSearchFilter) { d->mUi->txtFilter->setFocus(); } else { int sig; if(result == d->resume || result == d->sigCont) sig = SIGCONT; //Despite the function name, this sends a signal, rather than kill it. Silly unix :) else if(result == d->sigStop) sig = SIGSTOP; else if(result == d->sigHup) sig = SIGHUP; else if(result == d->sigInt) sig = SIGINT; else if(result == d->sigTerm) sig = SIGTERM; else if(result == d->sigKill) sig = SIGKILL; else if(result == d->sigUsr1) sig = SIGUSR1; else if(result == d->sigUsr2) sig = SIGUSR2; else return; sendSignalToSelectedProcesses(sig, false); } } void KSysGuardProcessList::selectAndJumpToProcess(int pid) { KSysGuard::Process *process = d->mModel.getProcess(pid); if(!process) return; QModelIndex sourceIndex = d->mModel.getQModelIndex(process, 0); QModelIndex filterIndex = d->mFilterModel.mapFromSource( sourceIndex ); if(!filterIndex.isValid() && !d->mUi->txtFilter->text().isEmpty()) { //The filter is preventing us from finding the parent. Clear the filter //(It could also be the combo box - should we deal with that case as well?) d->mUi->txtFilter->clear(); filterIndex = d->mFilterModel.mapFromSource( sourceIndex ); } d->mUi->treeView->clearSelection(); d->mUi->treeView->setCurrentIndex(filterIndex); d->mUi->treeView->scrollTo( filterIndex, QAbstractItemView::PositionAtCenter); } void KSysGuardProcessList::showColumnContextMenu(const QPoint &point){ QMenu menu; QAction *action; int num_headings = d->mFilterModel.columnCount(); int index = d->mUi->treeView->header()->logicalIndexAt(point); if(index >= 0) { bool anyOtherVisibleColumns = false; for(int i = 0; i < num_headings; ++i) { if(i != index && !d->mUi->treeView->header()->isSectionHidden(i)) { anyOtherVisibleColumns = true; break; } } if(anyOtherVisibleColumns) { //selected a column. Give the option to hide it action = new QAction(&menu); action->setData(-index-1); //We set data to be negative (and minus 1) to hide a column, and positive to show a column action->setText(i18n("Hide Column '%1'", d->mFilterModel.headerData(index, Qt::Horizontal, Qt::DisplayRole).toString())); menu.addAction(action); if(d->mUi->treeView->header()->sectionsHidden()) { menu.addSeparator(); } } } if(d->mUi->treeView->header()->sectionsHidden()) { for(int i = 0; i < num_headings; ++i) { if(d->mUi->treeView->header()->isSectionHidden(i)) { #ifndef HAVE_XRES if(i == ProcessModel::HeadingXMemory) continue; #endif action = new QAction(&menu); action->setText(i18n("Show Column '%1'", d->mFilterModel.headerData(i, Qt::Horizontal, Qt::DisplayRole).toString())); action->setData(i); //We set data to be negative (and minus 1) to hide a column, and positive to show a column menu.addAction(action); } } } QAction *actionAuto = NULL; QAction *actionKB = NULL; QAction *actionMB = NULL; QAction *actionGB = NULL; QAction *actionPercentage = NULL; QAction *actionShowCmdlineOptions = NULL; QAction *actionShowTooltips = NULL; QAction *actionNormalizeCPUUsage = NULL; QAction *actionIoCharacters = NULL; QAction *actionIoSyscalls = NULL; QAction *actionIoActualCharacters = NULL; QAction *actionIoShowRate = NULL; bool showIoRate = false; if(index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) showIoRate = d->mModel.ioInformation() == ProcessModel::BytesRate || d->mModel.ioInformation() == ProcessModel::SyscallsRate || d->mModel.ioInformation() == ProcessModel::ActualBytesRate; if( index == ProcessModel::HeadingVmSize || index == ProcessModel::HeadingMemory || index == ProcessModel::HeadingXMemory || index == ProcessModel::HeadingSharedMemory || ( (index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) && d->mModel.ioInformation() != ProcessModel::Syscalls)) { //If the user right clicks on a column that contains a memory size, show a toggle option for displaying //the memory in different units. e.g. "2000 k" or "2 m" menu.addSeparator()->setText(i18n("Display Units")); QActionGroup *unitsGroup = new QActionGroup(&menu); /* Automatic (human readable)*/ actionAuto = new QAction(&menu); actionAuto->setText(i18n("Mixed")); actionAuto->setCheckable(true); menu.addAction(actionAuto); unitsGroup->addAction(actionAuto); /* Kilobytes */ actionKB = new QAction(&menu); actionKB->setText((showIoRate)?i18n("Kilobytes per second"):i18n("Kilobytes")); actionKB->setCheckable(true); menu.addAction(actionKB); unitsGroup->addAction(actionKB); /* Megabytes */ actionMB = new QAction(&menu); actionMB->setText((showIoRate)?i18n("Megabytes per second"):i18n("Megabytes")); actionMB->setCheckable(true); menu.addAction(actionMB); unitsGroup->addAction(actionMB); /* Gigabytes */ actionGB = new QAction(&menu); actionGB->setText((showIoRate)?i18n("Gigabytes per second"):i18n("Gigabytes")); actionGB->setCheckable(true); menu.addAction(actionGB); unitsGroup->addAction(actionGB); ProcessModel::Units currentUnit; if(index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) { currentUnit = d->mModel.ioUnits(); } else { actionPercentage = new QAction(&menu); actionPercentage->setText(i18n("Percentage")); actionPercentage->setCheckable(true); menu.addAction(actionPercentage); unitsGroup->addAction(actionPercentage); currentUnit = d->mModel.units(); } switch(currentUnit) { case ProcessModel::UnitsAuto: actionAuto->setChecked(true); break; case ProcessModel::UnitsKB: actionKB->setChecked(true); break; case ProcessModel::UnitsMB: actionMB->setChecked(true); break; case ProcessModel::UnitsGB: actionGB->setChecked(true); break; case ProcessModel::UnitsPercentage: actionPercentage->setChecked(true); break; default: break; } unitsGroup->setExclusive(true); } else if(index == ProcessModel::HeadingName) { menu.addSeparator(); actionShowCmdlineOptions = new QAction(&menu); actionShowCmdlineOptions->setText(i18n("Display command line options")); actionShowCmdlineOptions->setCheckable(true); actionShowCmdlineOptions->setChecked(d->mModel.isShowCommandLineOptions()); menu.addAction(actionShowCmdlineOptions); } else if(index == ProcessModel::HeadingCPUUsage) { menu.addSeparator(); actionNormalizeCPUUsage = new QAction(&menu); actionNormalizeCPUUsage->setText(i18n("Divide CPU usage by number of CPUs")); actionNormalizeCPUUsage->setCheckable(true); actionNormalizeCPUUsage->setChecked(d->mModel.isNormalizedCPUUsage()); menu.addAction(actionNormalizeCPUUsage); } if(index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) { menu.addSeparator()->setText(i18n("Displayed Information")); QActionGroup *ioInformationGroup = new QActionGroup(&menu); actionIoCharacters = new QAction(&menu); actionIoCharacters->setText(i18n("Characters read/written")); actionIoCharacters->setCheckable(true); menu.addAction(actionIoCharacters); ioInformationGroup->addAction(actionIoCharacters); actionIoSyscalls = new QAction(&menu); actionIoSyscalls->setText(i18n("Number of Read/Write operations")); actionIoSyscalls->setCheckable(true); menu.addAction(actionIoSyscalls); ioInformationGroup->addAction(actionIoSyscalls); actionIoActualCharacters = new QAction(&menu); actionIoActualCharacters->setText(i18n("Bytes actually read/written")); actionIoActualCharacters->setCheckable(true); menu.addAction(actionIoActualCharacters); ioInformationGroup->addAction(actionIoActualCharacters); actionIoShowRate = new QAction(&menu); actionIoShowRate->setText(i18n("Show I/O rate")); actionIoShowRate->setCheckable(true); actionIoShowRate->setChecked(showIoRate); menu.addAction(actionIoShowRate); switch(d->mModel.ioInformation()) { case ProcessModel::Bytes: case ProcessModel::BytesRate: actionIoCharacters->setChecked(true); break; case ProcessModel::Syscalls: case ProcessModel::SyscallsRate: actionIoSyscalls->setChecked(true); break; case ProcessModel::ActualBytes: case ProcessModel::ActualBytesRate: actionIoActualCharacters->setChecked(true); break; default: break; } } menu.addSeparator(); actionShowTooltips = new QAction(&menu); actionShowTooltips->setCheckable(true); actionShowTooltips->setChecked(d->mModel.isShowingTooltips()); actionShowTooltips->setText(i18n("Show Tooltips")); menu.addAction(actionShowTooltips); QAction *result = menu.exec(d->mUi->treeView->header()->mapToGlobal(point)); if(!result) return; //Menu cancelled if(result == actionAuto) { if(index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) d->mModel.setIoUnits(ProcessModel::UnitsAuto); else d->mModel.setUnits(ProcessModel::UnitsAuto); return; } else if(result == actionKB) { if(index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) d->mModel.setIoUnits(ProcessModel::UnitsKB); else d->mModel.setUnits(ProcessModel::UnitsKB); return; } else if(result == actionMB) { if(index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) d->mModel.setIoUnits(ProcessModel::UnitsMB); else d->mModel.setUnits(ProcessModel::UnitsMB); return; } else if(result == actionGB) { if(index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) d->mModel.setIoUnits(ProcessModel::UnitsGB); else d->mModel.setUnits(ProcessModel::UnitsGB); return; } else if(result == actionPercentage) { d->mModel.setUnits(ProcessModel::UnitsPercentage); return; } else if(result == actionShowCmdlineOptions) { d->mModel.setShowCommandLineOptions(actionShowCmdlineOptions->isChecked()); return; } else if(result == actionNormalizeCPUUsage) { d->mModel.setNormalizedCPUUsage(actionNormalizeCPUUsage->isChecked()); return; } else if(result == actionShowTooltips) { d->mModel.setShowingTooltips(actionShowTooltips->isChecked()); return; } else if(result == actionIoCharacters) { d->mModel.setIoInformation((showIoRate)?ProcessModel::BytesRate:ProcessModel::Bytes); return; } else if(result == actionIoSyscalls) { d->mModel.setIoInformation((showIoRate)?ProcessModel::SyscallsRate:ProcessModel::Syscalls); return; } else if(result == actionIoActualCharacters) { d->mModel.setIoInformation((showIoRate)?ProcessModel::ActualBytesRate:ProcessModel::ActualBytes); return; } else if(result == actionIoShowRate) { showIoRate = actionIoShowRate->isChecked(); switch(d->mModel.ioInformation()) { case ProcessModel::Bytes: case ProcessModel::BytesRate: d->mModel.setIoInformation((showIoRate)?ProcessModel::BytesRate:ProcessModel::Bytes); break; case ProcessModel::Syscalls: case ProcessModel::SyscallsRate: d->mModel.setIoInformation((showIoRate)?ProcessModel::SyscallsRate:ProcessModel::Syscalls); break; case ProcessModel::ActualBytes: case ProcessModel::ActualBytesRate: d->mModel.setIoInformation((showIoRate)?ProcessModel::ActualBytesRate:ProcessModel::ActualBytes); break; default: break; } } int i = result->data().toInt(); //We set data to be negative to hide a column, and positive to show a column if(i < 0) d->mUi->treeView->hideColumn(-1-i); else { d->mUi->treeView->showColumn(i); updateList(); d->mUi->treeView->resizeColumnToContents(i); d->mUi->treeView->resizeColumnToContents(d->mFilterModel.columnCount()); } menu.deleteLater(); } void KSysGuardProcessList::expandAllChildren(const QModelIndex &parent) { //This is called when the user expands a node. This then expands all of its //children. This will trigger this function again recursively. QModelIndex sourceParent = d->mFilterModel.mapToSource(parent); for(int i = 0; i < d->mModel.rowCount(sourceParent); i++) { d->mUi->treeView->expand(d->mFilterModel.mapFromSource(d->mModel.index(i,0, sourceParent))); } } void KSysGuardProcessList::rowsInserted(const QModelIndex & parent, int start, int end ) { if(d->mModel.isSimpleMode() || parent.isValid()) { emit processListChanged(); return; //No tree or not a root node - no need to expand init } disconnect(&d->mFilterModel, &QAbstractItemModel::rowsInserted, this, &KSysGuardProcessList::rowsInserted); //It is a root node that we just inserted - expand it bool expanded = false; for(int i = start; i <= end; i++) { QModelIndex index = d->mFilterModel.index(i, 0, QModelIndex()); if(!d->mUi->treeView->isExpanded(index)) { if(!expanded) { disconnect(d->mUi->treeView, &QTreeView::expanded, this, &KSysGuardProcessList::expandAllChildren); expanded = true; } d->mUi->treeView->expand(index); d->mNeedToExpandInit = true; } } if(expanded) connect(d->mUi->treeView, &QTreeView::expanded, this, &KSysGuardProcessList::expandAllChildren); connect(&d->mFilterModel, &QAbstractItemModel::rowsInserted, this, &KSysGuardProcessList::rowsInserted); emit processListChanged(); } void KSysGuardProcessList::expandInit() { if(d->mModel.isSimpleMode()) return; //No tree - no need to expand init bool expanded = false; for(int i = 0; i < d->mFilterModel.rowCount(QModelIndex()); i++) { QModelIndex index = d->mFilterModel.index(i, 0, QModelIndex()); if(!d->mUi->treeView->isExpanded(index)) { if(!expanded) { disconnect(d->mUi->treeView, &QTreeView::expanded, this, &KSysGuardProcessList::expandAllChildren); expanded = true; } d->mUi->treeView->expand(index); } } if(expanded) connect(d->mUi->treeView, &QTreeView::expanded, this, &KSysGuardProcessList::expandAllChildren); } void KSysGuardProcessList::hideEvent ( QHideEvent * event ) //virtual protected from QWidget { //Stop updating the process list if we are hidden if(d->mUpdateTimer) d->mUpdateTimer->stop(); //stop any scripts running, to save on memory if(d->mScripting) d->mScripting->stopAllScripts(); QWidget::hideEvent(event); } void KSysGuardProcessList::showEvent ( QShowEvent * event ) //virtual protected from QWidget { //Start updating the process list again if we are shown again updateList(); QHeaderView *header = d->mUi->treeView->header(); d->mUi->treeView->sortByColumn(header->sortIndicatorSection(), header->sortIndicatorOrder()); QWidget::showEvent(event); } void KSysGuardProcessList::changeEvent( QEvent * event ) { if (event->type() == QEvent::LanguageChange) { d->mModel.retranslateUi(); d->mUi->retranslateUi(this); retranslateUi(); } QWidget::changeEvent(event); } void KSysGuardProcessList::retranslateUi() { d->mUi->cmbFilter->setItemIcon(ProcessFilter::AllProcesses, QIcon::fromTheme(QStringLiteral("view-process-all"))); d->mUi->cmbFilter->setItemIcon(ProcessFilter::AllProcessesInTreeForm, QIcon::fromTheme(QStringLiteral("view-process-all-tree"))); d->mUi->cmbFilter->setItemIcon(ProcessFilter::SystemProcesses, QIcon::fromTheme(QStringLiteral("view-process-system"))); d->mUi->cmbFilter->setItemIcon(ProcessFilter::UserProcesses, QIcon::fromTheme(QStringLiteral("view-process-users"))); d->mUi->cmbFilter->setItemIcon(ProcessFilter::OwnProcesses, QIcon::fromTheme(QStringLiteral("view-process-own"))); d->mUi->cmbFilter->setItemIcon(ProcessFilter::ProgramsOnly, QIcon::fromTheme(QStringLiteral("view-process-all"))); } void KSysGuardProcessList::updateList() { if(isVisible()) { KSysGuard::Processes::UpdateFlags updateFlags = KSysGuard::Processes::StandardInformation; if(!d->mUi->treeView->isColumnHidden(ProcessModel::HeadingIoRead) || !d->mUi->treeView->isColumnHidden(ProcessModel::HeadingIoWrite)) updateFlags |= KSysGuard::Processes::IOStatistics; if(!d->mUi->treeView->isColumnHidden(ProcessModel::HeadingXMemory)) updateFlags |= KSysGuard::Processes::XMemory; d->mModel.update(d->mUpdateIntervalMSecs, updateFlags); if(d->mUpdateTimer) d->mUpdateTimer->start(d->mUpdateIntervalMSecs); emit updated(); if (QToolTip::isVisible() && qApp->topLevelAt(QCursor::pos()) == window()) { QWidget *w = d->mUi->treeView->viewport(); if(w->geometry().contains(d->mUi->treeView->mapFromGlobal( QCursor::pos() ))) { QHelpEvent event(QEvent::ToolTip, w->mapFromGlobal( QCursor::pos() ), QCursor::pos()); qApp->notify(w, &event); } } if(--d->mResortCountDown <= 0) { d->mResortCountDown = 2; //resort every second time //resort now QHeaderView *header= d->mUi->treeView->header(); d->mUi->treeView->sortByColumn(header->sortIndicatorSection(), header->sortIndicatorOrder()); } if( d->mNeedToExpandInit ) { expandInit(); d->mNeedToExpandInit = false; } } } int KSysGuardProcessList::updateIntervalMSecs() const { return d->mUpdateIntervalMSecs; } void KSysGuardProcessList::setUpdateIntervalMSecs(int intervalMSecs) { if(intervalMSecs == d->mUpdateIntervalMSecs) return; d->mUpdateIntervalMSecs = intervalMSecs; if(intervalMSecs <= 0) { //no point keep the timer around if we aren't updating automatically delete d->mUpdateTimer; d->mUpdateTimer = NULL; return; } if(!d->mUpdateTimer) { //intervalMSecs is a valid time, so set up a timer d->mUpdateTimer = new QTimer(this); d->mUpdateTimer->setSingleShot(true); connect(d->mUpdateTimer, &QTimer::timeout, this, &KSysGuardProcessList::updateList); if(isVisible()) d->mUpdateTimer->start(d->mUpdateIntervalMSecs); } else d->mUpdateTimer->setInterval(d->mUpdateIntervalMSecs); } bool KSysGuardProcessList::reniceProcesses(const QList &pids, int niceValue) { QList< long long> unreniced_pids; for (int i = 0; i < pids.size(); ++i) { bool success = d->mModel.processController()->setNiceness(pids.at(i), niceValue); if(!success) { unreniced_pids << pids.at(i); } } if(unreniced_pids.isEmpty()) return true; //All processes were reniced successfully if(!d->mModel.isLocalhost()) return false; //We can't use kauth to renice non-localhost processes KAuth::Action action(QStringLiteral("org.kde.ksysguard.processlisthelper.renice")); action.setParentWidget(window()); d->setupKAuthAction( action, unreniced_pids); action.addArgument(QStringLiteral("nicevalue"), niceValue); KAuth::ExecuteJob *job = action.execute(); if (job->exec()) { updateList(); } else if (!job->exec()) { KMessageBox::sorry(this, i18n("You do not have the permission to renice the process and there " "was a problem trying to run as root. Error %1 %2", job->error(), job->errorString())); return false; } return true; } QList KSysGuardProcessList::selectedProcesses() const { QList processes; QModelIndexList selectedIndexes = d->mUi->treeView->selectionModel()->selectedRows(); for(int i = 0; i < selectedIndexes.size(); ++i) { KSysGuard::Process *process = reinterpret_cast (d->mFilterModel.mapToSource(selectedIndexes.at(i)).internalPointer()); processes << process; } return processes; } void KSysGuardProcessList::reniceSelectedProcesses() { QList pids; QPointer reniceDlg; { QList processes = selectedProcesses(); QStringList selectedAsStrings; if (processes.isEmpty()) { KMessageBox::sorry(this, i18n("You must select a process first.")); return; } int sched = -2; int iosched = -2; foreach(KSysGuard::Process *process, processes) { pids << process->pid(); selectedAsStrings << d->mModel.getStringForProcess(process); if(sched == -2) sched = (int)process->scheduler(); else if(sched != -1 && sched != (int)process->scheduler()) sched = -1; //If two processes have different schedulers, disable the cpu scheduler stuff if(iosched == -2) iosched = (int)process->ioPriorityClass(); else if(iosched != -1 && iosched != (int)process->ioPriorityClass()) iosched = -1; //If two processes have different schedulers, disable the cpu scheduler stuff } int firstPriority = processes.first()->niceLevel(); int firstIOPriority = processes.first()->ioniceLevel(); bool supportsIoNice = d->mModel.processController()->supportsIoNiceness(); if(!supportsIoNice) { iosched = -2; firstIOPriority = -2; } reniceDlg = new ReniceDlg(d->mUi->treeView, selectedAsStrings, firstPriority, sched, firstIOPriority, iosched); if(reniceDlg->exec() == QDialog::Rejected) { delete reniceDlg; return; } } //Because we've done into ReniceDlg, which calls processEvents etc, our processes list is no //longer valid QList renicePids; QList changeCPUSchedulerPids; QList changeIOSchedulerPids; foreach (long long pid, pids) { KSysGuard::Process *process = d->mModel.getProcess(pid); if (!process) continue; switch(reniceDlg->newCPUSched) { case -2: case -1: //Invalid, not changed etc. break; //So do nothing case KSysGuard::Process::Other: case KSysGuard::Process::Fifo: if(reniceDlg->newCPUSched != (int)process->scheduler()) { changeCPUSchedulerPids << pid; renicePids << pid; } else if(reniceDlg->newCPUPriority != process->niceLevel()) renicePids << pid; break; case KSysGuard::Process::RoundRobin: case KSysGuard::Process::Batch: if(reniceDlg->newCPUSched != (int)process->scheduler() || reniceDlg->newCPUPriority != process->niceLevel()) { changeCPUSchedulerPids << pid; } break; } switch(reniceDlg->newIOSched) { case -2: case -1: //Invalid, not changed etc. break; //So do nothing case KSysGuard::Process::None: if(reniceDlg->newIOSched != (int)process->ioPriorityClass()) { // Unfortunately linux doesn't actually let us set the ioniceness back to none after being set to something else if(process->ioPriorityClass() != KSysGuard::Process::BestEffort || reniceDlg->newIOPriority != process->ioniceLevel()) changeIOSchedulerPids << pid; } break; case KSysGuard::Process::Idle: if(reniceDlg->newIOSched != (int)process->ioPriorityClass()) { changeIOSchedulerPids << pid; } break; case KSysGuard::Process::BestEffort: if(process->ioPriorityClass() == KSysGuard::Process::None && reniceDlg->newIOPriority == (process->niceLevel() + 20)/5) break; //Don't set to BestEffort if it's on None and the nicelevel wouldn't change case KSysGuard::Process::RealTime: if(reniceDlg->newIOSched != (int)process->ioPriorityClass() || reniceDlg->newIOPriority != process->ioniceLevel()) { changeIOSchedulerPids << pid; } break; } } if(!changeCPUSchedulerPids.isEmpty()) { Q_ASSERT(reniceDlg->newCPUSched >= 0); if(!changeCpuScheduler(changeCPUSchedulerPids, (KSysGuard::Process::Scheduler) reniceDlg->newCPUSched, reniceDlg->newCPUPriority)) { delete reniceDlg; return; } } if(!renicePids.isEmpty()) { Q_ASSERT(reniceDlg->newCPUPriority <= 20 && reniceDlg->newCPUPriority >= -20); if(!reniceProcesses(renicePids, reniceDlg->newCPUPriority)) { delete reniceDlg; return; } } if(!changeIOSchedulerPids.isEmpty()) { if(!changeIoScheduler(changeIOSchedulerPids, (KSysGuard::Process::IoPriorityClass) reniceDlg->newIOSched, reniceDlg->newIOPriority)) { delete reniceDlg; return; } } delete reniceDlg; updateList(); } bool KSysGuardProcessList::changeIoScheduler(const QList< long long> &pids, KSysGuard::Process::IoPriorityClass newIoSched, int newIoSchedPriority) { if(newIoSched == KSysGuard::Process::None) newIoSched = KSysGuard::Process::BestEffort; if(newIoSched == KSysGuard::Process::Idle) newIoSchedPriority = 0; QList< long long> unchanged_pids; for (int i = 0; i < pids.size(); ++i) { bool success = d->mModel.processController()->setIoNiceness(pids.at(i), newIoSched, newIoSchedPriority); if(!success) { unchanged_pids << pids.at(i); } } if(unchanged_pids.isEmpty()) return true; if(!d->mModel.isLocalhost()) return false; //We can't use kauth to affect non-localhost processes KAuth::Action action(QStringLiteral("org.kde.ksysguard.processlisthelper.changeioscheduler")); action.setParentWidget(window()); d->setupKAuthAction( action, unchanged_pids); action.addArgument(QStringLiteral("ioScheduler"), (int)newIoSched); action.addArgument(QStringLiteral("ioSchedulerPriority"), newIoSchedPriority); KAuth::ExecuteJob *job = action.execute(); if (job->exec()) { updateList(); } else if (!job->exec()) { KMessageBox::sorry(this, i18n("You do not have the permission to change the I/O priority of the process and there " "was a problem trying to run as root. Error %1 %2", job->error(), job->errorString())); return false; } return true; } bool KSysGuardProcessList::changeCpuScheduler(const QList< long long> &pids, KSysGuard::Process::Scheduler newCpuSched, int newCpuSchedPriority) { if(newCpuSched == KSysGuard::Process::Other || newCpuSched == KSysGuard::Process::Batch) newCpuSchedPriority = 0; QList< long long> unchanged_pids; for (int i = 0; i < pids.size(); ++i) { bool success = d->mModel.processController()->setScheduler(pids.at(i), newCpuSched, newCpuSchedPriority); if(!success) { unchanged_pids << pids.at(i); } } if(unchanged_pids.isEmpty()) return true; if(!d->mModel.isLocalhost()) return false; //We can't use KAuth to affect non-localhost processes KAuth::Action action(QStringLiteral("org.kde.ksysguard.processlisthelper.changecpuscheduler")); action.setParentWidget(window()); d->setupKAuthAction( action, unchanged_pids); action.addArgument(QStringLiteral("cpuScheduler"), (int)newCpuSched); action.addArgument(QStringLiteral("cpuSchedulerPriority"), newCpuSchedPriority); KAuth::ExecuteJob *job = action.execute(); if (job->exec()) { updateList(); } else if (!job->exec()) { KMessageBox::sorry(this, i18n("You do not have the permission to change the CPU Scheduler for the process and there " "was a problem trying to run as root. Error %1 %2", job->error(), job->errorString())); return false; } return true; } bool KSysGuardProcessList::killProcesses(const QList< long long> &pids, int sig) { QList< long long> unkilled_pids; for (int i = 0; i < pids.size(); ++i) { bool success = d->mModel.processController()->sendSignal(pids.at(i), sig); // If we failed due to a reason other than insufficient permissions, elevating to root can't // help us if(!success && (d->mModel.processController()->lastError() == KSysGuard::Processes::InsufficientPermissions || d->mModel.processController()->lastError() == KSysGuard::Processes::Unknown)) { unkilled_pids << pids.at(i); } } if(unkilled_pids.isEmpty()) return true; if(!d->mModel.isLocalhost()) return false; //We can't elevate privileges to kill non-localhost processes KAuth::Action action(QStringLiteral("org.kde.ksysguard.processlisthelper.sendsignal")); action.setParentWidget(window()); d->setupKAuthAction( action, unkilled_pids); action.addArgument(QStringLiteral("signal"), sig); KAuth::ExecuteJob *job = action.execute(); if (job->exec()) { updateList(); } else if (!job->exec()) { KMessageBox::sorry(this, i18n("You do not have the permission to kill the process and there " "was a problem trying to run as root. Error %1 %2", job->error(), job->errorString())); return false; } return true; } void KSysGuardProcessList::killSelectedProcesses() { sendSignalToSelectedProcesses(SIGTERM, true); } void KSysGuardProcessList::sendSignalToSelectedProcesses(int sig, bool confirm) { QModelIndexList selectedIndexes = d->mUi->treeView->selectionModel()->selectedRows(); QStringList selectedAsStrings; QList< long long> selectedPids; QList processes = selectedProcesses(); foreach(KSysGuard::Process *process, processes) { selectedPids << process->pid(); if (!confirm) continue; QString name = d->mModel.getStringForProcess(process); selectedAsStrings << name; } if (selectedPids.isEmpty()) { if (confirm) KMessageBox::sorry(this, i18n("You must select a process first.")); return; } else if (confirm && (sig == SIGTERM || sig == SIGKILL)) { int count = selectedAsStrings.count(); QString msg; QString title; QString dontAskAgainKey; QString closeButton; if (sig == SIGTERM) { msg = i18np("Are you sure you want to end this process? Any unsaved work may be lost.", "Are you sure you want to end these %1 processes? Any unsaved work may be lost", count); title = i18ncp("Dialog title", "End Process", "End %1 Processes", count); dontAskAgainKey = QStringLiteral("endconfirmation"); closeButton = i18n("End"); } else if (sig == SIGKILL) { msg = i18np("Are you sure you want to immediately and forcibly kill this process? Any unsaved work may be lost.", "Are you sure you want to immediately and forcibly kill these %1 processes? Any unsaved work may be lost", count); title = i18ncp("Dialog title", "Forcibly Kill Process", "Forcibly Kill %1 Processes", count); dontAskAgainKey = QStringLiteral("killconfirmation"); closeButton = i18n("Kill"); } int res = KMessageBox::warningContinueCancelList(this, msg, selectedAsStrings, title, KGuiItem(closeButton, QStringLiteral("process-stop")), KStandardGuiItem::cancel(), dontAskAgainKey); if (res != KMessageBox::Continue) return; } //We have shown a GUI dialog box, which processes events etc. //So processes is NO LONGER VALID if (!killProcesses(selectedPids, sig)) return; if (sig == SIGTERM || sig == SIGKILL) { foreach (long long pid, selectedPids) { KSysGuard::Process *process = d->mModel.getProcess(pid); if (process) process->timeKillWasSent().start(); d->mUi->treeView->selectionModel()->clearSelection(); } } updateList(); } bool KSysGuardProcessList::showTotals() const { return d->mModel.showTotals(); } void KSysGuardProcessList::setShowTotals(bool showTotals) //slot { d->mModel.setShowTotals(showTotals); } ProcessModel::Units KSysGuardProcessList::units() const { return d->mModel.units(); } void KSysGuardProcessList::setUnits(ProcessModel::Units unit) { d->mModel.setUnits(unit); } void KSysGuardProcessList::saveSettings(KConfigGroup &cg) { /* Note that the ksysguard program does not use these functions. It saves the settings itself to an xml file instead */ cg.writeEntry("units", (int)(units())); cg.writeEntry("ioUnits", (int)(d->mModel.ioUnits())); cg.writeEntry("ioInformation", (int)(d->mModel.ioInformation())); cg.writeEntry("showCommandLineOptions", d->mModel.isShowCommandLineOptions()); cg.writeEntry("normalizeCPUUsage", d->mModel.isNormalizedCPUUsage()); cg.writeEntry("showTooltips", d->mModel.isShowingTooltips()); cg.writeEntry("showTotals", showTotals()); cg.writeEntry("filterState", (int)(state())); cg.writeEntry("updateIntervalMSecs", updateIntervalMSecs()); cg.writeEntry("headerState", d->mUi->treeView->header()->saveState()); //If we change, say, the header between versions of ksysguard, then the old headerState settings will not be valid. //The version property lets us keep track of which version we are cg.writeEntry("version", PROCESSHEADERVERSION); } void KSysGuardProcessList::loadSettings(const KConfigGroup &cg) { /* Note that the ksysguard program does not use these functions. It saves the settings itself to an xml file instead */ setUnits((ProcessModel::Units) cg.readEntry("units", (int)ProcessModel::UnitsKB)); d->mModel.setIoUnits((ProcessModel::Units) cg.readEntry("ioUnits", (int)ProcessModel::UnitsKB)); d->mModel.setIoInformation((ProcessModel::IoInformation) cg.readEntry("ioInformation", (int)ProcessModel::ActualBytesRate)); d->mModel.setShowCommandLineOptions(cg.readEntry("showCommandLineOptions", false)); d->mModel.setNormalizedCPUUsage(cg.readEntry("normalizeCPUUsage", true)); d->mModel.setShowingTooltips(cg.readEntry("showTooltips", true)); setShowTotals(cg.readEntry("showTotals", true)); setStateInt(cg.readEntry("filterState", (int)ProcessFilter::AllProcesses)); setUpdateIntervalMSecs(cg.readEntry("updateIntervalMSecs", 2000)); int version = cg.readEntry("version", 0); if(version == PROCESSHEADERVERSION) { //If the header has changed, the old settings are no longer valid. Only restore if version is the same restoreHeaderState(cg.readEntry("headerState", QByteArray())); } } void KSysGuardProcessList::restoreHeaderState(const QByteArray & state) { d->mUi->treeView->header()->restoreState(state); } bool KSysGuardProcessList::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if(obj == d->mUi->treeView) { if(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { d->mUi->treeView->selectionModel()->select(d->mUi->treeView->currentIndex(), QItemSelectionModel::Select | QItemSelectionModel::Rows); showProcessContextMenu(d->mUi->treeView->currentIndex()); return true; } else if(keyEvent->matches(QKeySequence::MoveToPreviousLine) || keyEvent->matches(QKeySequence::SelectPreviousLine) || keyEvent->matches(QKeySequence::MoveToPreviousPage) || keyEvent->matches(QKeySequence::SelectPreviousPage)) { if (d->mUi->treeView->selectionModel()->selectedRows().size() == 1 && d->mUi->treeView->selectionModel()->selectedRows().first().row() == 0) { // when first row is selected, pressing up or pgup moves to the textfield d->mUi->txtFilter->setFocus(); return true; } } else if (!keyEvent->text().isEmpty() && keyEvent->key() != Qt::Key_Tab && (!keyEvent->modifiers() || keyEvent->modifiers() == Qt::ShiftModifier)) { // move to textfield and forward keyevent if user starts typing from treeview d->mUi->txtFilter->setFocus(); QApplication::sendEvent(d->mUi->txtFilter, event); return true; } } else { Q_ASSERT(obj == d->mUi->txtFilter); if (d->mUi->treeView->model()->rowCount() == 0) { // treeview is empty, do nothing return false; } if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { // pressing enter will send enter to the first row in the list // the focusin eventfilter will make sure the first row is selected if there was // no previous selection d->mUi->treeView->setFocus(); QApplication::sendEvent(d->mUi->treeView, event); return true; } else if(keyEvent->matches(QKeySequence::MoveToNextLine) || keyEvent->matches(QKeySequence::SelectNextLine) || keyEvent->matches(QKeySequence::MoveToNextPage) || keyEvent->matches(QKeySequence::SelectNextPage)) { // attempting to move down by down-key or pgdown, or pressing enter will move focus // to the treeview d->mUi->treeView->setFocus(); return true; } } } return false; } ProcessModel *KSysGuardProcessList::processModel() { return &d->mModel; } void KSysGuardProcessList::setKillButtonVisible(bool visible) { d->mUi->btnKillProcess->setVisible(visible); } bool KSysGuardProcessList::isKillButtonVisible() const { return d->mUi->btnKillProcess->isVisible(); } bool KSysGuardProcessList::scriptingEnabled() const { return !!d->mScripting; } void KSysGuardProcessList::setScriptingEnabled(bool enabled) { if(!!d->mScripting == enabled) return; //Nothing changed if(!enabled) { delete d->mScripting; d->mScripting = NULL; } else { d->mScripting = new Scripting(this); d->mScripting->hide(); } } diff --git a/processui/ksysguardprocesslist.h b/processui/ksysguardprocesslist.h index c612b2d..8142a25 100644 --- a/processui/ksysguardprocesslist.h +++ b/processui/ksysguardprocesslist.h @@ -1,235 +1,235 @@ /* KSysGuard, the KDE System Guard Copyright (c) 1999, 2000 Chris Schlaeger Copyright (c) 2006 John Tapsell This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KSysGuardProcessList_h_ #define _KSysGuardProcessList_h_ #include #include #include #include "ProcessModel.h" #include "ProcessFilter.h" #include class QShowEvent; class QHideEvent; class QLineEdit; class QTreeView; struct KSysGuardProcessListPrivate; /** * This widget implements a process list page. Besides the process * list which is implemented as a ProcessList, it contains two * combo boxes and two buttons. The combo boxes are used to set the * update rate and the process filter. The buttons are used to force * an immediate update and to kill a process. */ class Q_DECL_EXPORT KSysGuardProcessList : public QWidget { Q_OBJECT Q_PROPERTY( bool showTotalsInTree READ showTotals WRITE setShowTotals ) Q_PROPERTY( ProcessFilter::State state READ state WRITE setState ) Q_PROPERTY( int updateIntervalMSecs READ updateIntervalMSecs WRITE setUpdateIntervalMSecs ) Q_PROPERTY( ProcessModel::Units units READ units WRITE setUnits ) Q_PROPERTY( bool killButtonVisible READ isKillButtonVisible WRITE setKillButtonVisible ) Q_PROPERTY( bool scriptingEnabled READ scriptingEnabled WRITE setScriptingEnabled ) Q_ENUMS( ProcessFilter::State ) Q_ENUMS( ProcessModel::Units ) public: KSysGuardProcessList(QWidget* parent = NULL, const QString &hostName = QString()); virtual ~KSysGuardProcessList(); QLineEdit *filterLineEdit() const; QTreeView *treeView() const; /** Returns which processes we are currently filtering for and the way in which we show them. * @see setState() */ ProcessFilter::State state() const; /** Returns the number of milliseconds that have to elapse before updating the list of processes. * If this is 0, the processes will not be automatically updated. */ int updateIntervalMSecs() const; /** Whether the widget will show child totals for CPU and Memory etc usage */ bool showTotals() const; /** The units to display memory sizes etc in. E.g. kb/mb/gb */ ProcessModel::Units units() const; /** Returns a list of the processes that have been selected by the user. */ QList selectedProcesses() const; /** Returns the number of processes currently being displayed * * To get the total number processes, visible or not, use processModel()-> * */ int visibleProcessesCount() const; /** Save the current state of the widget to the given config group * * @param[in] cg Config group to add these settings to * */ void saveSettings(KConfigGroup &cg); /** Load the saved state of the widget from the given config group */ void loadSettings(const KConfigGroup &cg); /** Returns the process model used. Use with caution. */ ProcessModel *processModel(); /** Restore the headings to the given state. */ void restoreHeaderState(const QByteArray & state); /** @returns whether the Kill Process button is visible. */ bool isKillButtonVisible() const; /** @param visible defines whether the Kill Process button is shown or not. */ void setKillButtonVisible(bool visible); /** Whether scripting support is enabled. * * Default is false. */ bool scriptingEnabled() const; /** Set whether scripting support is enabled. * * Default is false. */ void setScriptingEnabled(bool enabled); Q_SIGNALS: /** Emitted when the display has been updated */ void updated(); void processListChanged(); public Q_SLOTS: /** Inform the view that the user has changed the selection */ void selectionChanged(); /** Send a kill signal to all the processes that the user has selected. Pops up a dialog box to confirm with the user */ void killSelectedProcesses(); /** Send a signal to all the processes that the user has selected. * @p confirm - If true, pops up a dialog box to confirm with the user */ void sendSignalToSelectedProcesses(int sig, bool confirm); /** Send a signal to a list of given processes. * @p pids A list of PIDs that should be sent the signal * @p sig The signal to send. * @return Whether the kill went ahead. True if successful or user cancelled. False if there was a problem */ bool killProcesses(const QList< long long> &pids, int sig); /** Renice all the processes that the user has selected. Pops up a dialog box to ask for the nice value and confirm */ void reniceSelectedProcesses(); /** Change the CPU scheduler for the given of processes to the given scheduler, with the given scheduler priority. * If the scheduler is Other or Batch, @p newCpuSchedPriority is ignored. * @return Whether the cpu scheduler changing went ahead. True if successful or user cancelled. False if there was a problem */ bool changeCpuScheduler(const QList< long long> &pids, KSysGuard::Process::Scheduler newCpuSched, int newCpuSchedPriority); /** Change the I/O scheduler for the given of processes to the given scheduler, with the given scheduler priority. * If the scheduler is Other or Batch, @p newCpuSchedPriority is ignored. * @return Whether the cpu scheduler changing went ahead. True if successful or user cancelled. False if there was a problem */ bool changeIoScheduler(const QList< long long> &pids, KSysGuard::Process::IoPriorityClass newIoSched, int newIoSchedPriority); /** Renice the processes given to the given niceValue. * @return Whether the kill went ahead. True if successful or user cancelled. False if there was a problem * */ bool reniceProcesses(const QList &pids, int niceValue); /** Fetch new process information and redraw the display */ void updateList(); /** Set which processes we are currently filtering for and the way in which we show them. */ void setState(ProcessFilter::State state); /** Set the number of milliseconds that have to elapse before updating the list of processes. * If this is set to 0, the process list will not be automatically updated and the owner can call * updateList() manually. */ void setUpdateIntervalMSecs(int intervalMSecs); /** Set whether to show child totals for CPU and Memory etc usage */ void setShowTotals(bool showTotals); /** Focus on a particular process, and select it */ void selectAndJumpToProcess(int pid); /** The units to display memory sizes etc in. */ void setUnits(ProcessModel::Units unit); /** Row was just inserted in the filter model */ void rowsInserted ( const QModelIndex & parent, int start, int end ); private Q_SLOTS: /** Expand all the children, recursively, of the node given. Pass an empty QModelIndex to expand all the top level children */ void expandAllChildren(const QModelIndex &parent); /** Expand init to show its children, but not the sub children processes. */ void expandInit(); /** Display a context menu for the column headings allowing the user to show or hide columns. */ void showColumnContextMenu(const QPoint &point); /** Display a context menu for the given process allowing the user to kill etc the process */ void showProcessContextMenu(const QModelIndex &index); /** Display a context menu for the selected processes allowing the user to kill etc the process */ void showProcessContextMenu(const QPoint &point); /** Set state from combo box int value */ void setStateInt(int state); /** Called when the text in the gui filter text box has changed */ void filterTextChanged(const QString &newText); /** Called when one of the actions (kill, renice etc) is clicked etc */ void actionTriggered(QObject *object); protected: /** Inherit QWidget::showEvent(QShowEvent *) to enable the timer, for updates, when visible */ - virtual void showEvent(QShowEvent*); + void showEvent(QShowEvent*) Q_DECL_OVERRIDE; /** Inherit QWidget::hideEvent(QShowEvent *) to disable the timer, for updates, when not visible */ - virtual void hideEvent(QHideEvent*); + void hideEvent(QHideEvent*) Q_DECL_OVERRIDE; /** Capture any change events sent to this widget. In particular QEvent::LanguageChange */ - virtual void changeEvent ( QEvent * event ); + void changeEvent ( QEvent * event ) Q_DECL_OVERRIDE; - bool eventFilter(QObject *obj, QEvent *event); + bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; /** Retranslate the Ui as needed */ void retranslateUi(); private: KSysGuardProcessListPrivate* const d; }; Q_DECLARE_METATYPE( long long ) Q_DECLARE_METATYPE( QList ) #endif diff --git a/signalplotter/kgraphicssignalplotter.h b/signalplotter/kgraphicssignalplotter.h index 2f3177a..77f0e21 100644 --- a/signalplotter/kgraphicssignalplotter.h +++ b/signalplotter/kgraphicssignalplotter.h @@ -1,462 +1,462 @@ /* This file is part of the KDE project Copyright (c) 2006 - 2009 John Tapsell This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KGRAPHICSSIGNALPLOTTER_H #define KGRAPHICSSIGNALPLOTTER_H #include #include #include #include #include #include class QGraphicsSceneResizeEvent; class KGraphicsSignalPlotterPrivate; //Make sure only declare KLocalizedString once #ifndef KSIGNALPLOTTER_H Q_DECLARE_METATYPE(KLocalizedString) #endif /** \class KGraphicsSignalPlotter * \brief The KGraphicsSignalPlotter graphics widget draws a real time graph of data that updates continually. * * Features include: * \li Points are joined by a bezier curve. * \li Lines are anti-aliased * \li Background can be set as a specified SVG * \li The lines can be reordered * \li Uses as little memory and CPU as possible * \li Graph can be smoothed using the formula (value * 2 + last_value)/3 * \li Can cope with positive/negative infinity and NaN values. * * Example usage: * \code * KGraphicsSignalPlotter *s = KGraphicsSignalPlotter(parent); * s->addBeam(Qt::blue); * s->addBeam(Qt::green); * QList data; * data << 4.0 << 5.0; * s->addSample(data); * \endcode * * Note that the number of horizontal lines is calculated automatically based on the axis font size, even if the axis labels are not shown. * * Smoothing looks very nice visually and is enabled by default. It can be disabled with setSmoothGraph(). * * \image KSignalPlotter.png Example KGraphicsSignalPlotter with two beams */ class Q_DECL_EXPORT KGraphicsSignalPlotter : public QGraphicsWidget { Q_OBJECT Q_PROPERTY(qreal minimumValue READ minimumValue WRITE setMinimumValue) Q_PROPERTY(qreal maximumValue READ maximumValue WRITE setMaximumValue) Q_PROPERTY(bool useAutoRange READ useAutoRange WRITE setUseAutoRange) Q_PROPERTY(KLocalizedString unit READ unit WRITE setUnit) Q_PROPERTY(qreal scaleDownBy READ scaleDownBy WRITE setScaleDownBy) Q_PROPERTY(uint horizontalScale READ horizontalScale WRITE setHorizontalScale) Q_PROPERTY(bool showHorizontalLines READ showHorizontalLines WRITE setShowHorizontalLines) Q_PROPERTY(bool showVerticalLines READ showVerticalLines WRITE setShowVerticalLines) Q_PROPERTY(bool verticalLinesScroll READ verticalLinesScroll WRITE setVerticalLinesScroll) Q_PROPERTY(uint verticalLinesDistance READ verticalLinesDistance WRITE setVerticalLinesDistance) Q_PROPERTY(bool showAxis READ showAxis WRITE setShowAxis) Q_PROPERTY(QString svgBackground READ svgBackground WRITE setSvgBackground) Q_PROPERTY(bool thinFrame READ thinFrame WRITE setThinFrame) Q_PROPERTY(int maxAxisTextWidth READ maxAxisTextWidth WRITE setMaxAxisTextWidth) Q_PROPERTY(bool smoothGraph READ smoothGraph WRITE setSmoothGraph) Q_PROPERTY(bool stackGraph READ stackGraph WRITE setStackGraph) Q_PROPERTY(int fillOpacity READ fillOpacity WRITE setFillOpacity) public: KGraphicsSignalPlotter( QGraphicsItem *parent = 0); virtual ~KGraphicsSignalPlotter(); /** \brief Add a new line to the graph plotter, with the specified color. * * Note that the order you add the beams in must be the same order that * the beam data is given in (Unless you reorder the beams). * * \param color Color of beam - does not have to be unique. */ void addBeam( const QColor &color ); /** \brief Add data to the graph, and advance the graph by one time period. * * The data must be given as a list in the same order that the beams were * added (or consequently reordered). If samples.count() != numBeams(), * a warning is printed and the data discarded. * * To indicate that no data is available for a given beam, set its value to * (non-signalling) NaN. * * For example: * * \code * KSignalPlotter *s = KSignalPlotter(parent); * s->addBeam(Qt::red); * s->addBeam(Qt::green); * s->addBeam(Qt::blue); * signalPlotter->addSample( QList() << std::numeric_limits::quiet_NaN() << 1.0/0 << 10.0 ); * \endcode * * This indicates that no data is available yet for red (so the beam will not be drawn for this section), * that's it positive infinity for green, and 10 for blue. * * Infinity is handled by drawing a straight line up to the top or bottom of the display, and does not affect the range. * For the above example, the displayed range would now be 0 to 10. */ void addSample( const QList &samples ); /** \brief Reorder the beams into the order given. * * For example: * \code * KSignalPlotter *s = KSignalPlotter(parent); * s->addBeam(Qt::blue); * s->addBeam(Qt::green); * s->addBeam(Qt::red); * QList neworder; * neworder << 2 << 0 << 1; * s->reorderBeams( newOrder); * //Now the order is red, blue then green * \endcode * * The size of the \p newOrder list must be equal to the result of numBeams(). * \param newOrder New order of beams. */ void reorderBeams( const QList& newOrder ); /** \brief Removes the beam at the specified index. * * This causes the graph to be redrawn with the specified beam completely * removed. */ void removeBeam( int index ); /** \brief Get the color of the beam at the specified index. * * For example: * \code * KSignalPlotter *s = KSignalPlotter(parent); * s->addBeam(Qt::blue); * s->addBeam(Qt::green); * s->addBeam(Qt::red); * * QColor color = s->beamColor(0); //returns blue * \endcode * * \sa setBeamColor() */ QColor beamColor( int index ) const; /** \brief Set the color of the beam at the specified index. * * \sa beamColor() */ void setBeamColor( int index, const QColor &color ); /** \brief Returns the number of beams. */ int numBeams() const; /** \brief Set the axis units with a localized string. * * The localized string must contain a placeholder "%1" which is substituted for the value. * The plural form (ki18np) can be used if the unit string changes depending on the number (for example * "1 second", "2 seconds"). * * For example: * * \code * KSignalPlotter plotter; * plotter.setUnit( ki18ncp("Units", "%1 second", "%1 seconds") ); * QString formattedString = plotter.valueAsString(3.4); //returns "3.4 seconds" * \endcode * * Typically a new unit would be set when setScaleDownBy is called. * Note that even the singular should use "%1 second" instead of "1 second", so that a value of -1 works correctly. * * \see unit(), setScaleDownBy() */ void setUnit( const KLocalizedString &unit ); /** \brief The localizable units used on the vertical axis of the graph. * * The returns the localizable string set with setUnit(). * * Default is the string "%1" - i.e. to just display the number. * * \see setUnit */ KLocalizedString unit() const; /** \brief Scale all the values down by the given amount. * * This is useful when the data is given in, say, kilobytes, but you set * the units as megabytes. Thus you would have to call this with @p value * set to 1024. This affects all the data already entered. * * Typically this is followed by calling setUnit() to set the display axis * units. Default value is 1. */ void setScaleDownBy( qreal value ); /** \brief Amount scaled down by. * * \sa setScaleDownBy */ qreal scaleDownBy() const; /** \brief Set whether to scale the graph automatically beyond the given range. * * If true, the range on vertical axis is automatically expanded from the * data available, expanding beyond the range set by changeRange() if data * values are outside of this range. * * Regardless whether this is set of not, the range of the vertical axis * will never be less than the range given by maximumValue() and minimumvalue(). * * \param value Whether to scale beyond the given range. Default is true. * * \sa useAutoRange */ void setUseAutoRange( bool value ); /** \brief Whether the vertical axis range is set automatically. */ bool useAutoRange() const; /** \brief Change the minimum and maximum values drawn on the graph. * * Note that these values are sanitised. For example, if you * set the minimum as 3, and the maximum as 97, then the graph * would be drawn between 0 and 100. The algorithm to determine * this "nice range" attempts to minimize the number of non-zero * digits. * * If autoRange() is true, then this range is taking as a 'hint'. * The range will never be smaller than the given range, but can grow * if there are values larger than the given range. * * This is equivalent to calling * \code * setMinimumValue(min); * setMaximumValue(max); * \endcode * * \sa setMinimumValue(), setMaximumValue(), minimumValue(), maximumValue() */ void changeRange( qreal min, qreal max ); /** \brief Set the min value hint for the vertical axis. * * \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */ void setMinimumValue( qreal min ); /** \brief Get the min value hint for the vertical axis. * * \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */ qreal minimumValue() const; /** \brief Set the max value hint for the vertical axis. * * * \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */ void setMaximumValue( qreal max ); /** \brief Get the maximum value hint for the vertical axis. * * \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */ qreal maximumValue() const; /** \brief Get the current maximum value on the y-axis. * * This will never be lower than maximumValue(), and if autoRange() is true, * it will be equal or larger (due to rounding up to make it a nice number) * than the highest value being shown. */ qreal currentMaximumRangeValue() const; /** \brief Get the current minimum value on the y-axis. * * This will never be lower than minimumValue(), and if autoRange() is true, * it will be equal or larger (due to rounding up to make it a nice number) * than the highest value being shown. */ qreal currentMinimumRangeValue() const; /** \brief Set the number of pixels horizontally between data points. * Default is 6. */ void setHorizontalScale( uint scale ); /** \brief The number of pixels horizontally between data points. * Default is 6. */ int horizontalScale() const; /** \brief Set whether to draw the vertical grid lines. * Default is false. */ void setShowVerticalLines( bool value ); /** \brief Whether to draw the vertical grid lines. * Default is false. */ bool showVerticalLines() const; /** \brief Set the horizontal distance, in pixels, between the vertical grid lines. * Must be a distance of 1 or more. * Default is 30 pixels. */ void setVerticalLinesDistance( uint distance ); /** \brief The horizontal distance, in pixels, between the vertical grid lines. * Default is 30 pixels. */ uint verticalLinesDistance() const; /** \brief Set whether the vertical lines move with the data. * Default is true. This has no effect is showVerticalLines is false. */ void setVerticalLinesScroll( bool value ); /** \brief Whether the vertical lines move with the data. * Default is true. This has no effect is showVerticalLines is false. */ bool verticalLinesScroll() const; /** \brief Set whether to draw the horizontal grid lines. * Default is true. */ void setShowHorizontalLines( bool value ); /** \brief Whether to draw the horizontal grid lines. * Default is true. */ bool showHorizontalLines() const; /** \brief The number of decimal places used for the axis labels * * This is calculated based on the current range */ int currentAxisPrecision() const; /** \brief Set whether to show the vertical axis labels. * * Default is true. * \sa showAxis(), setAxisFont(), setAxisFontColor(), setMaxAxisTextWidth() */ void setShowAxis( bool show ); /** \brief Whether to show the vertical axis labels. * * Default is true. * \sa setShowAxis(), axisFont(), axisFontColor(), maxAxisTextWidth() */ bool showAxis() const; /** \brief Set the filename of the SVG background. * * Set to empty (default) to disable again. */ void setSvgBackground( const QString &filename ); /** \brief The filename of the SVG background. */ QString svgBackground() const; /** \brief Return the last value that we have for the given beam index. * * \return last value, or 0 if not known. */ qreal lastValue( int index) const; /** \brief Return a translated string for the last value at the given index. * * Returns, for example, "34 %" or "100 KB" for the given beam index, * using the last value set for the beam, using the given precision. * * If precision is -1 (the default) then if @p value is greater than 99.5, no decimal figures are shown, * otherwise if @p value is greater than 0.995, 1 decimal figure is used, otherwise 2. */ QString lastValueAsString( int index, int precision = -1) const; /** \brief Return a translated string for the given value. * * Returns, for example, "34 %" or "100 KB" for the given value in unscaled units. * * If precision is -1 (the default) then if @p value is greater than 99.5, no decimal figures are shown, * otherwise if @p value is greater than 0.995, 1 decimal figure is used, otherwise 2. * * For example: * \code * KSignalPlotter plotter; * plotter.setUnit( ki18ncp("Units", "1 hour", "%1 hours") ); * plotter.scaleDownBy( 60 ); //The input will be in seconds, and there's 60 seconds in an hour * QString formattedString = plotter.valueAsString(150); //returns "2.5 hours" * \endcode * */ QString valueAsString( qreal value, int precision = -1) const; /** \brief Set the distance between the left of the widget and the left of the plotting region. * * For example: * \code * int axisTextWidth = fontMetrics().width(i18nc("Largest axis title", "99999 XXXX")); * plotter->setMaxAxisTextWidth(axisTextWidth); * \endcode * * If this is 0, the default, then the text will be shown inside the plotting area. */ void setMaxAxisTextWidth(int maxAxisTextWidth); /** \brief Get the distance between the left of the widget and the left of the plotting region. */ int maxAxisTextWidth() const; /** \brief Set whether to smooth the graph by averaging the points. * * This uses the formula: (value*2 + last_value)/3. * Default is true. */ void setSmoothGraph(bool smooth); /** \brief Whether to smooth the graph by averaging the points. * * This uses the formula: (value*2 + last_value)/3. * Default is true. */ bool smoothGraph() const; /** \brief Set whether to stack the beams on top of each other. * * Default is false */ void setStackGraph(bool stack); /** \brief Whether to stack the beams on top of each other. * * Default is false */ bool stackGraph() const; /** \brief Alpha value for filling the area underneath the graph lines. * * Set to 0 to disable filling the graph, and 255 for a solid fill. Default is 20*/ void setFillOpacity(int fill); /** \brief Alpha value for filling the area underneath the graph lines. */ int fillOpacity() const; /* Whether to show a thin line on the left and bottom of the widget, for a slight 3D effect */ bool thinFrame() const; /* Set whether to show a thin line on the left and bottom of the widget, for a slight 3D effect */ void setThinFrame(bool thinFrame); Q_SIGNALS: /** When the axis has changed this signal is emitted. */ void axisScaleChanged(); protected: /* Reimplemented */ - virtual void resizeEvent( QGraphicsSceneResizeEvent* event ); - virtual void paint( QPainter * p, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ); - virtual QSizeF sizeHint( Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; - virtual void changeEvent ( QEvent * event ); - virtual QPainterPath opaqueArea() const; + void resizeEvent( QGraphicsSceneResizeEvent* event ) Q_DECL_OVERRIDE; + void paint( QPainter * p, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ) Q_DECL_OVERRIDE; + QSizeF sizeHint( Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const Q_DECL_OVERRIDE; + void changeEvent ( QEvent * event ) Q_DECL_OVERRIDE; + QPainterPath opaqueArea() const Q_DECL_OVERRIDE; private: KGraphicsSignalPlotterPrivate * const d; friend class KGraphicsSignalPlotterPrivate; }; #endif diff --git a/signalplotter/ksignalplotter.h b/signalplotter/ksignalplotter.h index 274cf12..9165094 100644 --- a/signalplotter/ksignalplotter.h +++ b/signalplotter/ksignalplotter.h @@ -1,462 +1,462 @@ /* This file is part of the KDE project Copyright (c) 2006 - 2009 John Tapsell This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KSIGNALPLOTTER_H #define KSIGNALPLOTTER_H #include #include #include #include #include #include class QPaintEvent; class QResizeEvent; struct KSignalPlotterPrivate; //Make sure only declare KLocalizedString once #ifndef KGRAPHICSSIGNALPLOTTER_H Q_DECLARE_METATYPE(KLocalizedString) #endif /** \class KSignalPlotter * \brief The KSignalPlotter widget draws a real time graph of data that updates continually. * * Features include: * \li Points are joined by a bezier curve. * \li Lines are anti-aliased * \li Background can be set as a specified SVG * \li The lines can be reordered * \li Uses as little memory and CPU as possible * \li Graph can be smoothed using the formula (value * 2 + last_value)/3 * \li Can cope with positive/negative infinity and NaN values. * * Example usage: * \code * KSignalPlotter *s = KSignalPlotter(parent); * s->addBeam(Qt::blue); * s->addBeam(Qt::green); * QList data; * data << 4.0 << 5.0; * s->addSample(data); * \endcode * * Note that the number of horizontal lines is calculated automatically based on the axis font size, even if the axis labels are not shown. * * Smoothing looks very nice visually and is enabled by default. It can be disabled with setSmoothGraph(). * * \image KSignalPlotter.png Example KSignalPlotter with two beams */ class Q_DECL_EXPORT KSignalPlotter : public QWidget { Q_OBJECT Q_PROPERTY(qreal minimumValue READ minimumValue WRITE setMinimumValue) Q_PROPERTY(qreal maximumValue READ maximumValue WRITE setMaximumValue) Q_PROPERTY(bool useAutoRange READ useAutoRange WRITE setUseAutoRange) Q_PROPERTY(KLocalizedString unit READ unit WRITE setUnit) Q_PROPERTY(qreal scaleDownBy READ scaleDownBy WRITE setScaleDownBy) Q_PROPERTY(uint horizontalScale READ horizontalScale WRITE setHorizontalScale) Q_PROPERTY(bool showHorizontalLines READ showHorizontalLines WRITE setShowHorizontalLines) Q_PROPERTY(bool showVerticalLines READ showVerticalLines WRITE setShowVerticalLines) Q_PROPERTY(bool verticalLinesScroll READ verticalLinesScroll WRITE setVerticalLinesScroll) Q_PROPERTY(uint verticalLinesDistance READ verticalLinesDistance WRITE setVerticalLinesDistance) Q_PROPERTY(bool showAxis READ showAxis WRITE setShowAxis) Q_PROPERTY(QString svgBackground READ svgBackground WRITE setSvgBackground) Q_PROPERTY(bool thinFrame READ thinFrame WRITE setThinFrame) Q_PROPERTY(int maxAxisTextWidth READ maxAxisTextWidth WRITE setMaxAxisTextWidth) Q_PROPERTY(bool smoothGraph READ smoothGraph WRITE setSmoothGraph) Q_PROPERTY(bool stackGraph READ stackGraph WRITE setStackGraph) Q_PROPERTY(int fillOpacity READ fillOpacity WRITE setFillOpacity) public: KSignalPlotter( QWidget *parent = 0); virtual ~KSignalPlotter(); /** \brief Add a new line to the graph plotter, with the specified color. * * Note that the order you add the beams in must be the same order that * the beam data is given in (Unless you reorder the beams). * * \param color Color of beam - does not have to be unique. */ void addBeam( const QColor &color ); /** \brief Add data to the graph, and advance the graph by one time period. * * The data must be given as a list in the same order that the beams were * added (or consequently reordered). If samples.count() != numBeams(), * a warning is printed and the data discarded. * * To indicate that no data is available for a given beam, set its value to * (non-signalling) NaN. * * For example: * * \code * KSignalPlotter *s = KSignalPlotter(parent); * s->addBeam(Qt::red); * s->addBeam(Qt::green); * s->addBeam(Qt::blue); * signalPlotter->addSample( QList() << std::numeric_limits::quiet_NaN() << 1.0/0 << 10.0 ); * \endcode * * This indicates that no data is available yet for red (so the beam will not be drawn for this section), * that's it positive infinity for green, and 10 for blue. * * Infinity is handled by drawing a straight line up to the top or bottom of the display, and does not affect the range. * For the above example, the displayed range would now be 0 to 10. */ void addSample( const QList &samples ); /** \brief Reorder the beams into the order given. * * For example: * \code * KSignalPlotter *s = KSignalPlotter(parent); * s->addBeam(Qt::blue); * s->addBeam(Qt::green); * s->addBeam(Qt::red); * QList neworder; * neworder << 2 << 0 << 1; * s->reorderBeams( newOrder); * //Now the order is red, blue then green * \endcode * * The size of the \p newOrder list must be equal to the result of numBeams(). * \param newOrder New order of beams. */ void reorderBeams( const QList& newOrder ); /** \brief Removes the beam at the specified index. * * This causes the graph to be redrawn with the specified beam completely * removed. */ void removeBeam( int index ); /** \brief Get the color of the beam at the specified index. * * For example: * \code * KSignalPlotter *s = KSignalPlotter(parent); * s->addBeam(Qt::blue); * s->addBeam(Qt::green); * s->addBeam(Qt::red); * * QColor color = s->beamColor(0); //returns blue * \endcode * * \sa setBeamColor() */ QColor beamColor( int index ) const; /** \brief Set the color of the beam at the specified index. * * \sa beamColor() */ void setBeamColor( int index, const QColor &color ); /** \brief Returns the number of beams. */ int numBeams() const; /** \brief Set the axis units with a localized string. * * The localized string must contain a placeholder "%1" which is substituted for the value. * The plural form (ki18np) can be used if the unit string changes depending on the number (for example * "1 second", "2 seconds"). * * For example: * * \code * KSignalPlotter plotter; * plotter.setUnit( ki18ncp("Units", "%1 second", "%1 seconds") ); * QString formattedString = plotter.valueAsString(3.4); //returns "3.4 seconds" * \endcode * * Typically a new unit would be set when setScaleDownBy is called. * Note that even the singular should use "%1 second" instead of "1 second", so that a value of -1 works correctly. * * \see unit(), setScaleDownBy() */ void setUnit( const KLocalizedString &unit ); /** \brief The localizable units used on the vertical axis of the graph. * * The returns the localizable string set with setUnit(). * * Default is the string "%1" - i.e. to just display the number. * * \see setUnit */ KLocalizedString unit() const; /** \brief Scale all the values down by the given amount. * * This is useful when the data is given in, say, kilobytes, but you set * the units as megabytes. Thus you would have to call this with @p value * set to 1024. This affects all the data already entered. * * Typically this is followed by calling setUnit() to set the display axis * units. Default value is 1. */ void setScaleDownBy( qreal value ); /** \brief Amount scaled down by. * * \sa setScaleDownBy */ qreal scaleDownBy() const; /** \brief Set whether to scale the graph automatically beyond the given range. * * If true, the range on vertical axis is automatically expanded from the * data available, expanding beyond the range set by changeRange() if data * values are outside of this range. * * Regardless whether this is set of not, the range of the vertical axis * will never be less than the range given by maximumValue() and minimumvalue(). * * \param value Whether to scale beyond the given range. Default is true. * * \sa useAutoRange */ void setUseAutoRange( bool value ); /** \brief Whether the vertical axis range is set automatically. */ bool useAutoRange() const; /** \brief Change the minimum and maximum values drawn on the graph. * * Note that these values are sanitised. For example, if you * set the minimum as 3, and the maximum as 97, then the graph * would be drawn between 0 and 100. The algorithm to determine * this "nice range" attempts to minimize the number of non-zero * digits. * * If autoRange() is true, then this range is taking as a 'hint'. * The range will never be smaller than the given range, but can grow * if there are values larger than the given range. * * This is equivalent to calling * \code * setMinimumValue(min); * setMaximumValue(max); * \endcode * * \sa setMinimumValue(), setMaximumValue(), minimumValue(), maximumValue() */ void changeRange( qreal min, qreal max ); /** \brief Set the min value hint for the vertical axis. * * \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */ void setMinimumValue( qreal min ); /** \brief Get the min value hint for the vertical axis. * * \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */ qreal minimumValue() const; /** \brief Set the max value hint for the vertical axis. * * * \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */ void setMaximumValue( qreal max ); /** \brief Get the maximum value hint for the vertical axis. * * \sa changeRange(), minimumValue(), setMaximumValue(), maximumValue() */ qreal maximumValue() const; /** \brief Get the current maximum value on the y-axis. * * This will never be lower than maximumValue(), and if autoRange() is true, * it will be equal or larger (due to rounding up to make it a nice number) * than the highest value being shown. */ qreal currentMaximumRangeValue() const; /** \brief Get the current minimum value on the y-axis. * * This will never be lower than minimumValue(), and if autoRange() is true, * it will be equal or larger (due to rounding up to make it a nice number) * than the highest value being shown. */ qreal currentMinimumRangeValue() const; /** \brief Set the number of pixels horizontally between data points. * Default is 6. */ void setHorizontalScale( uint scale ); /** \brief The number of pixels horizontally between data points. * Default is 6. */ int horizontalScale() const; /** \brief Set whether to draw the vertical grid lines. * Default is false. */ void setShowVerticalLines( bool value ); /** \brief Whether to draw the vertical grid lines. * Default is false. */ bool showVerticalLines() const; /** \brief Set the horizontal distance, in pixels, between the vertical grid lines. * Must be a distance of 1 or more. * Default is 30 pixels. */ void setVerticalLinesDistance( uint distance ); /** \brief The horizontal distance, in pixels, between the vertical grid lines. * Default is 30 pixels. */ uint verticalLinesDistance() const; /** \brief Set whether the vertical lines move with the data. * Default is true. This has no effect is showVerticalLines is false. */ void setVerticalLinesScroll( bool value ); /** \brief Whether the vertical lines move with the data. * Default is true. This has no effect is showVerticalLines is false. */ bool verticalLinesScroll() const; /** \brief Set whether to draw the horizontal grid lines. * Default is true. */ void setShowHorizontalLines( bool value ); /** \brief Whether to draw the horizontal grid lines. * Default is true. */ bool showHorizontalLines() const; /** \brief The number of decimal places used for the axis labels * * This is calculated based on the current range */ int currentAxisPrecision() const; /** \brief Set whether to show the vertical axis labels. * * Default is true. * \sa showAxis(), setAxisFont(), setAxisFontColor(), setMaxAxisTextWidth() */ void setShowAxis( bool show ); /** \brief Whether to show the vertical axis labels. * * Default is true. * \sa setShowAxis(), axisFont(), axisFontColor(), maxAxisTextWidth() */ bool showAxis() const; /** \brief Set the filename of the SVG background. * * Set to empty (default) to disable again. */ void setSvgBackground( const QString &filename ); /** \brief The filename of the SVG background. */ QString svgBackground() const; /** \brief Return the last value that we have for the given beam index. * * \return last value, or 0 if not known. */ qreal lastValue( int index) const; /** \brief Return a translated string for the last value at the given index. * * Returns, for example, "34 %" or "100 KB" for the given beam index, * using the last value set for the beam, using the given precision. * * If precision is -1 (the default) then if @p value is greater than 99.5, no decimal figures are shown, * otherwise if @p value is greater than 0.995, 1 decimal figure is used, otherwise 2. */ QString lastValueAsString( int index, int precision = -1) const; /** \brief Return a translated string for the given value. * * Returns, for example, "34 %" or "100 KB" for the given value in unscaled units. * * If precision is -1 (the default) then if @p value is greater than 99.5, no decimal figures are shown, * otherwise if @p value is greater than 0.995, 1 decimal figure is used, otherwise 2. * * For example: * \code * KSignalPlotter plotter; * plotter.setUnit( ki18ncp("Units", "1 hour", "%1 hours") ); * plotter.scaleDownBy( 60 ); //The input will be in seconds, and there's 60 seconds in an hour * QString formattedString = plotter.valueAsString(150); //returns "2.5 hours" * \endcode * */ QString valueAsString( qreal value, int precision = -1) const; /** \brief Set the distance between the left of the widget and the left of the plotting region. * * For example: * \code * int axisTextWidth = fontMetrics().width(i18nc("Largest axis title", "99999 XXXX")); * plotter->setMaxAxisTextWidth(axisTextWidth); * \endcode * * If this is 0, the default, then the text will be shown inside the plotting area. */ void setMaxAxisTextWidth(int maxAxisTextWidth); /** \brief Get the distance between the left of the widget and the left of the plotting region. */ int maxAxisTextWidth() const; /** \brief Set whether to smooth the graph by averaging the points. * * This uses the formula: (value*2 + last_value)/3. * Default is true. */ void setSmoothGraph(bool smooth); /** \brief Whether to smooth the graph by averaging the points. * * This uses the formula: (value*2 + last_value)/3. * Default is true. */ bool smoothGraph() const; /** \brief Set whether to stack the beams on top of each other. * * Default is false */ void setStackGraph(bool stack); /** \brief Whether to stack the beams on top of each other. * * Default is false */ bool stackGraph() const; /** \brief Alpha value for filling the area underneath the graph lines. * * Set to 0 to disable filling the graph, and 255 for a solid fill. Default is 20*/ void setFillOpacity(int fill); /** \brief Alpha value for filling the area underneath the graph lines. */ int fillOpacity() const; /* Whether to show a thin line on the left and bottom of the widget, for a slight 3D effect */ bool thinFrame() const; /* Set whether to show a thin line on the left and bottom of the widget, for a slight 3D effect */ void setThinFrame(bool thinFrame); Q_SIGNALS: /** When the axis has changed this signal is emitted. */ void axisScaleChanged(); protected: /* Reimplemented */ - virtual void resizeEvent( QResizeEvent* ); - virtual void paintEvent( QPaintEvent* ); - virtual QSize sizeHint() const; - virtual void changeEvent ( QEvent * event ); + void resizeEvent( QResizeEvent* ) Q_DECL_OVERRIDE; + void paintEvent( QPaintEvent* ) Q_DECL_OVERRIDE; + QSize sizeHint() const Q_DECL_OVERRIDE; + void changeEvent ( QEvent * event ) Q_DECL_OVERRIDE; private: KSignalPlotterPrivate * const d; friend struct KSignalPlotterPrivate; }; #endif diff --git a/signalplotter/ksignalplotter_p.h b/signalplotter/ksignalplotter_p.h index a99cdb9..a2ff1ce 100644 --- a/signalplotter/ksignalplotter_p.h +++ b/signalplotter/ksignalplotter_p.h @@ -1,152 +1,152 @@ /* This file is part of the KDE project Copyright (c) 2006 - 2009 John Tapsell This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ //#define USE_QIMAGE // SVG support causes it to crash at the moment :( //#define SVG_SUPPORT // Use a seperate child widget to draw the graph in #ifndef GRAPHICS_SIGNAL_PLOTTER #define USE_SEPERATE_WIDGET #include #include #endif #ifdef SVG_SUPPORT namespace Plasma { class SVG; } #endif #ifdef USE_SEPERATE_WIDGET class GraphWidget; #endif class KSignalPlotter; struct KSignalPlotterPrivate { KSignalPlotterPrivate( KSignalPlotter * q_ptr ); void drawWidget(QPainter *p, const QRect &boundingBox); void drawBackground(QPainter *p, const QRect & boundingBox) const; void drawThinFrame(QPainter *p, const QRect &boundingBox); void calculateNiceRange(); void drawBeamToScrollableImage(QPainter *p, int index); void drawBeam(QPainter *p, const QRect &boundingBox, int horizontalScale, int index); void drawAxisText(QPainter *p, const QRect &boundingBox); void drawHorizontalLines(QPainter *p, const QRect &boundingBox) const; void drawVerticalLines(QPainter *p, const QRect &boundingBox, int correction=0) const; void redrawScrollableImage(); void reorderBeams( const QList& newOrder ); void recalculateMaxMinValueForSample(const QList&sampleBuf, int time ); void rescale(); void updateDataBuffers(); void setupStyle(); #ifdef GRAPHICS_SIGNAL_PLOTTER void themeChanged(); #endif /** Return the given value as a string, with the given precision */ QString scaledValueAsString( qreal value, int precision) const; void addSample( const QList& sampleBuf ); #ifdef SVG_SUPPORT void updateSvgBackground(const QRect &boundingBox); Plasma::SVG* mSvgRenderer; #endif QString mSvgFilename; QPixmap mBackgroundImage; ///A cache of the background of the widget. Contains the SVG or just white background with lines #ifdef USE_QIMAGE QImage mScrollableImage; ///The scrollable image for the widget. Contains the SVG lines #else QPixmap mScrollableImage; ///The scrollable image for the widget. Contains the SVG lines #endif int mScrollOffset; ///The scrollable image is, well, scrolled in a wrap-around window. mScrollOffset determines where the left hand side of the mScrollableImage should be drawn relative to the right hand side of view. 0 <= mScrollOffset < mScrollableImage.width() qreal mMinValue; ///The minimum value (unscaled) currently being displayed qreal mMaxValue; ///The maximum value (unscaled) currently being displayed qreal mUserMinValue; ///The minimum value (unscaled) set by changeRange(). This is the _maximum_ value that the range will start from. qreal mUserMaxValue; ///The maximum value (unscaled) set by changeRange(). This is the _minimum_ value that the range will reach to. unsigned int mRescaleTime; ///The number of data points passed since a value that is within 70% of the current maximum was found. This is for scaling the graph qreal mNiceMinValue; ///The minimum value rounded down to a 'nice' value qreal mNiceMaxValue; ///The maximum value rounded up to a 'nice' value. The idea is to round the value, say, 93 to 100. qreal mNiceRange; /// mNiceMaxValue - mNiceMinValue int mPrecision; ///The number of decimal place required to unambiguously label the axis qreal mScaleDownBy; /// @see setScaleDownBy bool mUseAutoRange; /// @see setUseAutoRange /** Whether to show a white line on the left and bottom of the widget, for a 3D effect */ bool mShowThinFrame; bool mShowVerticalLines; uint mVerticalLinesDistance; bool mVerticalLinesScroll; uint mVerticalLinesOffset; uint mHorizontalScale; int mHorizontalLinesCount; bool mShowHorizontalLines; bool mStackBeams; /// Set to add the beam values onto each other int mFillOpacity; /// Fill the area underneath the beams bool mShowAxis; QList < QList > mBeamData; // Every item in the linked list contains a set of data points to plot. The first item is the newest QList< QColor> mBeamColors; //These colors match up against the QList in mBeamData QList< QColor> mBeamColorsLight; //These colors match up against the QList in mBeamData, and are lighter than mBeamColors. Done for gradient effects unsigned int mMaxSamples; //This is what mBeamData.size() should equal when full. When we start off and have no data then mSamples will be higher. If we resize the widget so it's smaller, then for a short while this will be smaller int mNewestIndex; //The index to the newest item added. newestIndex+1 is the second newest, and so on KLocalizedString mUnit; int mAxisTextWidth; int mActualAxisTextWidth; // Sometimes there just is not enough room for all the requested axisTextWidth QRect mPlottingArea; /// The area in which the beams are drawn. Saved to make update() more efficient bool mSmoothGraph; /// Whether to smooth the graph by averaging using the formula (value*2 + last_value)/3. KSignalPlotter *q; bool mAxisTextOverlapsPlotter; // Whether we need to redraw the axis text on every update #ifdef USE_SEPERATE_WIDGET GraphWidget *mGraphWidget; ///< This is the widget that draws the actual graph #endif }; #ifdef USE_SEPERATE_WIDGET /* A class to draw the actual widget. This is used for the QWidget version of KSignalPlotter in order to speed up redraws */ class GraphWidget : public QWidget { public: GraphWidget(QWidget *parent); - virtual void paintEvent ( QPaintEvent * event ); + void paintEvent ( QPaintEvent * event ) Q_DECL_OVERRIDE; KSignalPlotterPrivate *signalPlotterPrivate; }; #endif