Merge pull request #13723 from spowelljr/gui

Added initial GUI
pull/13752/head
Steven Powell 2022-03-03 19:05:58 +00:00 committed by GitHub
commit b6120e1802
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1356 additions and 0 deletions

87
.clang-format Normal file
View File

@ -0,0 +1,87 @@
# Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
#
# You may use this file under the terms of the 3-clause BSD license.
# See the file LICENSE from this package for details.
# This is the clang-format configuration style to be used by Qt,
# based on the rules from https://wiki.qt.io/Qt_Coding_Style and
# https://wiki.qt.io/Coding_Conventions
---
# Webkit style was loosely based on the Qt style
BasedOnStyle: WebKit
Standard: Cpp11
# Column width is limited to 100 in accordance with Qt Coding Style.
# https://wiki.qt.io/Qt_Coding_Style
# Note that this may be changed at some point in the future.
ColumnLimit: 100
# How much weight do extra characters after the line length limit have.
# PenaltyExcessCharacter: 4
# Disable reflow of qdoc comments: indentation rules are different.
# Translation comments are also excluded.
CommentPragmas: "^!|^:"
# We want a space between the type and the star for pointer types.
PointerBindsToType: false
# We use template< without space.
SpaceAfterTemplateKeyword: false
# We want to break before the operators, but not before a '='.
BreakBeforeBinaryOperators: NonAssignment
# Braces are usually attached, but not after functions or class declarations.
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
# When constructor initializers do not fit on one line, put them each on a new line.
ConstructorInitializerAllOnOneLineOrOnePerLine: true
# Indent initializers by 4 spaces
ConstructorInitializerIndentWidth: 4
# Indent width for line continuations.
ContinuationIndentWidth: 8
# No indentation for namespaces.
NamespaceIndentation: None
# Allow indentation for preprocessing directives (if/ifdef/endif). https://reviews.llvm.org/rL312125
#IndentPPDirectives: AfterHash
# AfterHash is broken, since it indents every single include statement not following after an ifdef
IndentPPDirectives: None
# Horizontally align arguments after an open bracket.
# The coding style does not specify the following, but this is what gives
# results closest to the existing code.
AlignAfterOpenBracket: true
AlwaysBreakTemplateDeclarations: true
# Ideally we should also allow less short function in a single line, but
# clang-format does not handle that.
AllowShortFunctionsOnASingleLine: Inline
# The coding style specifies some include order categories, but also tells to
# separate categories with an empty line. It does not specify the order within
# the categories. Since the SortInclude feature of clang-format does not
# re-order includes separated by empty lines, the feature is not used.
SortIncludes: false
# macros for which the opening brace stays attached.
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ]
# Break constructor initializers before the colon and after the commas.
BreakConstructorInitializers: BeforeColon

30
gui/LICENSE Normal file
View File

@ -0,0 +1,30 @@
Copyright 2022 The Kubernetes Authors All rights reserved.
Copyright (C) 2021 Anders F Björklund
Copyright (C) 2016 The Qt Company Ltd.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
gui/README.md Normal file
View File

@ -0,0 +1,22 @@
# System Tray Icon
## Running
Open in [Qt Creator](https://doc.qt.io/qtcreator/) GUI:
```shell
qtcreator systray.pro
```
Or on the command line:
```console
$ qmake
$ make
...
$ ./systray
```
----
See <https://doc.qt.io/qt-5/qtwidgets-desktop-systray-example.html>

145
gui/cluster.cpp Normal file
View File

@ -0,0 +1,145 @@
/****************************************************************************
**
** Copyright 2022 The Kubernetes Authors All rights reserved.
**
** Copyright (C) 2021 Anders F Björklund
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "cluster.h"
#include <QStringList>
void ClusterModel::setClusters(const ClusterList &clusters)
{
beginResetModel();
clusterList = clusters;
endResetModel();
}
int ClusterModel::rowCount(const QModelIndex &) const
{
return clusterList.count();
}
int ClusterModel::columnCount(const QModelIndex &) const
{
return 6;
}
static QStringList binaryAbbrs = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB" };
QVariant ClusterModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= clusterList.size())
return QVariant();
if (index.column() >= 6)
return QVariant();
if (role == Qt::TextAlignmentRole) {
switch (index.column()) {
case 0:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
case 1:
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case 2:
// fall-through
case 3:
// fall-through
case 4:
// fall-through
case 5:
return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
}
}
if (role == Qt::DisplayRole) {
Cluster cluster = clusterList.at(index.row());
switch (index.column()) {
case 0:
return cluster.name();
case 1:
return cluster.status();
case 2:
return cluster.driver();
case 3:
return cluster.containerRuntime();
case 4:
return QString::number(cluster.cpus());
case 5:
return QString::number(cluster.memory());
}
}
return QVariant();
}
QVariant ClusterModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
switch (section) {
case 0:
return tr("Name");
case 1:
return tr("Status");
case 2:
return tr("Driver");
case 3:
return tr("Container Runtime");
case 4:
return tr("CPUs");
case 5:
return tr("Memory (MB)");
}
}
return QVariant(); // QStringLiteral("Row %1").arg(section);
}

120
gui/cluster.h Normal file
View File

@ -0,0 +1,120 @@
/****************************************************************************
**
** Copyright 2022 The Kubernetes Authors All rights reserved.
**
** Copyright (C) 2021 Anders F Björklund
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef CLUSTER_H
#define CLUSTER_H
#include <QAbstractListModel>
#include <QString>
#include <QList>
#include <QMap>
//! [0]
class Cluster
{
public:
Cluster() : Cluster("") { }
Cluster(const QString &name)
: m_name(name), m_status(""), m_driver(""), m_container_runtime(""), m_cpus(0), m_memory(0)
{
}
QString name() const { return m_name; }
QString status() const { return m_status; }
void setStatus(QString status) { m_status = status; }
QString driver() const { return m_driver; }
void setDriver(QString driver) { m_driver = driver; }
QString containerRuntime() const { return m_container_runtime; }
void setContainerRuntime(QString containerRuntime) { m_container_runtime = containerRuntime; }
int cpus() const { return m_cpus; }
void setCpus(int cpus) { m_cpus = cpus; }
int memory() const { return m_memory; }
void setMemory(int memory) { m_memory = memory; }
private:
QString m_name;
QString m_status;
QString m_driver;
QString m_container_runtime;
int m_cpus;
int m_memory;
};
//! [0]
typedef QList<Cluster> ClusterList;
typedef QHash<QString, Cluster> ClusterHash;
//! [1]
class ClusterModel : public QAbstractListModel
{
Q_OBJECT
public:
ClusterModel(const ClusterList &clusters, QObject *parent = nullptr)
: QAbstractListModel(parent), clusterList(clusters)
{
}
void setClusters(const ClusterList &clusters);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
private:
ClusterList clusterList;
};
//! [1]
#endif // CLUSTER_H

BIN
gui/images/minikube.icns Normal file

Binary file not shown.

BIN
gui/images/minikube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

101
gui/main.cpp Normal file
View File

@ -0,0 +1,101 @@
/****************************************************************************
**
** Copyright 2022 The Kubernetes Authors All rights reserved.
**
** Copyright (C) 2021 Anders F Björklund
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QApplication>
#ifndef QT_NO_SYSTEMTRAYICON
#include <QMessageBox>
#include "window.h"
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(systray);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication app(argc, argv);
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
QMessageBox::critical(0, QObject::tr("Systray"),
QObject::tr("I couldn't detect any system tray "
"on this system."));
return 1;
}
QApplication::setQuitOnLastWindowClosed(false);
Window window;
window.show();
return app.exec();
}
#else
#include <QLabel>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QString text("QSystemTrayIcon is not supported on this platform");
QLabel *label = new QLabel(text);
label->setWordWrap(true);
label->show();
qDebug() << text;
app.exec();
}
#endif

25
gui/systray.pro Normal file
View File

@ -0,0 +1,25 @@
HEADERS = window.h \
cluster.h
SOURCES = main.cpp \
cluster.cpp \
window.cpp
RESOURCES = systray.qrc
ICON = images/minikube.icns
QT += widgets
requires(qtConfig(combobox))
DISTFILES += \
LICENSE
# Enabling qtermwidget requires GPL-v2 license
#CONFIG += gpl_licensed
gpl_licensed {
win32: DEFINES += QT_NO_TERMWIDGET
unix: CONFIG += link_pkgconfig
unix: PKGCONFIG += qtermwidget5
} else {
DEFINES += QT_NO_TERMWIDGET
}

5
gui/systray.qrc Normal file
View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>images/minikube.png</file>
</qresource>
</RCC>

596
gui/window.cpp Normal file
View File

@ -0,0 +1,596 @@
/****************************************************************************
**
** Copyright 2022 The Kubernetes Authors All rights reserved.
**
** Copyright (C) 2021 Anders F Björklund
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "window.h"
#ifndef QT_NO_SYSTEMTRAYICON
#include <QAction>
#include <QCheckBox>
#include <QComboBox>
#include <QCoreApplication>
#include <QCloseEvent>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QPushButton>
#include <QSpinBox>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QProcess>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QTableView>
#include <QHeaderView>
#include <QFormLayout>
#include <QDialogButtonBox>
#include <QStandardPaths>
#ifndef QT_NO_TERMWIDGET
#include <QApplication>
#include <QMainWindow>
#include "qtermwidget.h"
#endif
//! [0]
Window::Window()
{
createClusterGroupBox();
createActions();
createTrayIcon();
connect(sshButton, &QAbstractButton::clicked, this, &Window::sshConsole);
connect(dashboardButton, &QAbstractButton::clicked, this, &Window::dashboardBrowser);
connect(startButton, &QAbstractButton::clicked, this, &Window::startMinikube);
connect(stopButton, &QAbstractButton::clicked, this, &Window::stopMinikube);
connect(deleteButton, &QAbstractButton::clicked, this, &Window::deleteMinikube);
connect(refreshButton, &QAbstractButton::clicked, this, &Window::updateClusters);
connect(createButton, &QAbstractButton::clicked, this, &Window::initMachine);
connect(trayIcon, &QSystemTrayIcon::messageClicked, this, &Window::messageClicked);
dashboardProcess = 0;
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(clusterGroupBox);
setLayout(mainLayout);
trayIcon->show();
setWindowTitle(tr("minikube"));
setWindowIcon(*trayIconIcon);
resize(600, 400);
}
//! [0]
//! [1]
void Window::setVisible(bool visible)
{
minimizeAction->setEnabled(visible);
restoreAction->setEnabled(!visible);
QDialog::setVisible(visible);
}
//! [1]
//! [2]
void Window::closeEvent(QCloseEvent *event)
{
#ifdef Q_OS_OSX
if (!event->spontaneous() || !isVisible()) {
return;
}
#endif
if (trayIcon->isVisible()) {
QMessageBox::information(this, tr("Systray"),
tr("The program will keep running in the "
"system tray. To terminate the program, "
"choose <b>Quit</b> in the context menu "
"of the system tray entry."));
hide();
event->ignore();
}
}
//! [2]
//! [6]
void Window::messageClicked()
{
QMessageBox::information(0, tr("Systray"),
tr("Sorry, I already gave what help I could.\n"
"Maybe you should try asking a human?"));
}
//! [6]
void Window::createActions()
{
minimizeAction = new QAction(tr("Mi&nimize"), this);
connect(minimizeAction, &QAction::triggered, this, &QWidget::hide);
restoreAction = new QAction(tr("&Restore"), this);
connect(restoreAction, &QAction::triggered, this, &QWidget::showNormal);
quitAction = new QAction(tr("&Quit"), this);
connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);
}
static QString minikubePath()
{
QString program = QStandardPaths::findExecutable("minikube");
if (program.isEmpty()) {
QStringList paths = { "/usr/local/bin" };
program = QStandardPaths::findExecutable("minikube", paths);
}
return program;
}
void Window::createTrayIcon()
{
trayIconMenu = new QMenu(this);
trayIconMenu->addAction(minimizeAction);
trayIconMenu->addAction(restoreAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
trayIconIcon = new QIcon(":/images/minikube.png");
trayIcon = new QSystemTrayIcon(this);
trayIcon->setContextMenu(trayIconMenu);
trayIcon->setIcon(*trayIconIcon);
}
void Window::startMinikube()
{
QStringList args = { "start", "-p", selectedCluster() };
sendMinikubeCommand(args);
updateClusters();
}
void Window::stopMinikube()
{
QStringList args = { "stop", "-p", selectedCluster() };
sendMinikubeCommand(args);
updateClusters();
}
void Window::deleteMinikube()
{
QStringList args = { "delete", "-p", selectedCluster() };
sendMinikubeCommand(args);
updateClusters();
}
void Window::updateClusters()
{
QString cluster = selectedCluster();
clusterModel->setClusters(getClusters());
setSelectedCluster(cluster);
updateButtons();
}
ClusterList Window::getClusters()
{
ClusterList clusters;
QStringList args = { "profile", "list", "-o", "json" };
QString text;
bool success = sendMinikubeCommand(args, text);
QStringList lines;
if (success) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
lines = text.split("\n", Qt::SkipEmptyParts);
#else
lines = text.split("\n", QString::SkipEmptyParts);
#endif
}
for (int i = 0; i < lines.size(); i++) {
QString line = lines.at(i);
QJsonParseError error;
QJsonDocument json = QJsonDocument::fromJson(line.toUtf8(), &error);
if (json.isNull()) {
qDebug() << error.errorString();
continue;
}
if (json.isObject()) {
QJsonObject par = json.object();
QJsonArray a = par["valid"].toArray();
for (int j = 0; j < a.size(); j++) {
QJsonObject obj = a[j].toObject();
QString name;
if (obj.contains("Name")) {
name = obj["Name"].toString();
}
if (name.isEmpty()) {
continue;
}
Cluster cluster(name);
if (obj.contains("Status")) {
QString status = obj["Status"].toString();
cluster.setStatus(status);
}
if (!obj.contains("Config")) {
clusters << cluster;
continue;
}
QJsonObject config = obj["Config"].toObject();
if (config.contains("CPUs")) {
int cpus = config["CPUs"].toInt();
cluster.setCpus(cpus);
}
if (config.contains("Memory")) {
int memory = config["Memory"].toInt();
cluster.setMemory(memory);
}
if (config.contains("Driver")) {
QString driver = config["Driver"].toString();
cluster.setDriver(driver);
}
if (!config.contains("KubernetesConfig")) {
clusters << cluster;
continue;
}
QJsonObject k8sConfig = config["KubernetesConfig"].toObject();
if (k8sConfig.contains("ContainerRuntime")) {
QString containerRuntime = k8sConfig["ContainerRuntime"].toString();
cluster.setContainerRuntime(containerRuntime);
}
clusters << cluster;
}
}
}
return clusters;
}
QString Window::selectedCluster()
{
QModelIndex index = clusterListView->currentIndex();
QVariant variant = index.data(Qt::DisplayRole);
if (variant.isNull()) {
return QString();
}
return variant.toString();
}
void Window::setSelectedCluster(QString cluster)
{
QAbstractItemModel *model = clusterListView->model();
QModelIndex start = model->index(0, 0);
QModelIndexList index = model->match(start, Qt::DisplayRole, cluster);
if (index.size() == 0) {
return;
}
clusterListView->setCurrentIndex(index[0]);
}
void Window::createClusterGroupBox()
{
clusterGroupBox = new QGroupBox(tr("Clusters"));
ClusterList clusters = getClusters();
clusterModel = new ClusterModel(clusters);
clusterListView = new QTableView();
clusterListView->setModel(clusterModel);
clusterListView->setSelectionMode(QAbstractItemView::SingleSelection);
clusterListView->setSelectionBehavior(QAbstractItemView::SelectRows);
clusterListView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
clusterListView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
clusterListView->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
clusterListView->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
clusterListView->horizontalHeader()->setSectionResizeMode(4, QHeaderView::ResizeToContents);
clusterListView->horizontalHeader()->setSectionResizeMode(5, QHeaderView::ResizeToContents);
setSelectedCluster("default");
connect(clusterListView, SIGNAL(clicked(QModelIndex)), this, SLOT(updateButtons()));
startButton = new QPushButton(tr("Start"));
stopButton = new QPushButton(tr("Stop"));
deleteButton = new QPushButton(tr("Delete"));
refreshButton = new QPushButton(tr("Refresh"));
createButton = new QPushButton(tr("Create"));
sshButton = new QPushButton(tr("SSH"));
dashboardButton = new QPushButton(tr("Dashboard"));
updateButtons();
QHBoxLayout *topButtonLayout = new QHBoxLayout;
topButtonLayout->addWidget(createButton);
topButtonLayout->addWidget(refreshButton);
topButtonLayout->addSpacing(340);
QHBoxLayout *bottomButtonLayout = new QHBoxLayout;
bottomButtonLayout->addWidget(startButton);
bottomButtonLayout->addWidget(stopButton);
bottomButtonLayout->addWidget(deleteButton);
bottomButtonLayout->addWidget(sshButton);
bottomButtonLayout->addWidget(dashboardButton);
QVBoxLayout *clusterLayout = new QVBoxLayout;
clusterLayout->addLayout(topButtonLayout);
clusterLayout->addWidget(clusterListView);
clusterLayout->addLayout(bottomButtonLayout);
clusterGroupBox->setLayout(clusterLayout);
}
void Window::updateButtons()
{
QString cluster = selectedCluster();
if (cluster.isEmpty()) {
startButton->setEnabled(false);
stopButton->setEnabled(false);
deleteButton->setEnabled(false);
sshButton->setEnabled(false);
dashboardButton->setEnabled(false);
return;
}
deleteButton->setEnabled(true);
Cluster clusterHash = getClusterHash()[cluster];
if (clusterHash.status() == "Running") {
startButton->setEnabled(false);
stopButton->setEnabled(true);
#if __linux__
sshButton->setEnabled(true);
#endif
dashboardButton->setEnabled(true);
} else {
startButton->setEnabled(true);
stopButton->setEnabled(false);
}
}
ClusterHash Window::getClusterHash()
{
ClusterList clusters = getClusters();
ClusterHash clusterHash;
for (int i = 0; i < clusters.size(); i++) {
Cluster cluster = clusters.at(i);
clusterHash[cluster.name()] = cluster;
}
return clusterHash;
}
bool Window::sendMinikubeCommand(QStringList cmds)
{
QString text;
return sendMinikubeCommand(cmds, text);
}
bool Window::sendMinikubeCommand(QStringList cmds, QString &text)
{
QString program = minikubePath();
if (program.isEmpty()) {
return false;
}
QStringList arguments;
arguments << cmds;
bool success;
QProcess *process = new QProcess(this);
process->start(program, arguments);
this->setCursor(Qt::WaitCursor);
success = process->waitForFinished(300 * 1000);
this->unsetCursor();
if (success) {
text = process->readAllStandardOutput();
} else {
qDebug() << process->readAllStandardOutput();
qDebug() << process->readAllStandardError();
}
delete process;
return success;
}
static QString profile = "minikube";
static int cpus = 2;
static int memory = 2400;
static QString driver = "";
static QString containerRuntime = "";
void Window::askName()
{
QDialog dialog;
dialog.setWindowTitle(tr("Create minikube Cluster"));
dialog.setModal(true);
QFormLayout form(&dialog);
QDialogButtonBox buttonBox(Qt::Horizontal, &dialog);
QLineEdit profileField(profile, &dialog);
form.addRow(new QLabel(tr("Profile")), &profileField);
buttonBox.addButton(QString(tr("Use Default Values")), QDialogButtonBox::AcceptRole);
connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
buttonBox.addButton(QString(tr("Set Custom Values")), QDialogButtonBox::RejectRole);
connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
form.addRow(&buttonBox);
int code = dialog.exec();
profile = profileField.text();
if (code == QDialog::Accepted) {
QStringList arg = { "start", "-p", profile };
sendMinikubeCommand(arg);
} else if (code == QDialog::Rejected) {
askCustom();
}
}
void Window::askCustom()
{
QDialog dialog;
dialog.setWindowTitle(tr("Set Cluster Values"));
dialog.setModal(true);
QFormLayout form(&dialog);
driverComboBox = new QComboBox;
driverComboBox->addItem("docker");
#if __linux__
driverComboBox->addItem("kvm2");
#elif __APPLE__
driverComboBox->addItem("hyperkit");
driverComboBox->addItem("parallels");
#else
driverComboBox->addItem("hyperv");
#endif
driverComboBox->addItem("virtualbox");
driverComboBox->addItem("vmware");
driverComboBox->addItem("podman");
form.addRow(new QLabel(tr("Driver")), driverComboBox);
containerRuntimeComboBox = new QComboBox;
containerRuntimeComboBox->addItem("docker");
containerRuntimeComboBox->addItem("containerd");
containerRuntimeComboBox->addItem("crio");
form.addRow(new QLabel(tr("Container Runtime")), containerRuntimeComboBox);
QLineEdit cpuField(QString::number(cpus), &dialog);
form.addRow(new QLabel(tr("CPUs")), &cpuField);
QLineEdit memoryField(QString::number(memory), &dialog);
form.addRow(new QLabel(tr("Memory")), &memoryField);
QDialogButtonBox buttonBox(Qt::Horizontal, &dialog);
buttonBox.addButton(QString(tr("Create")), QDialogButtonBox::AcceptRole);
connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
buttonBox.addButton(QString(tr("Cancel")), QDialogButtonBox::RejectRole);
connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
form.addRow(&buttonBox);
int code = dialog.exec();
if (code == QDialog::Accepted) {
driver = driverComboBox->itemText(driverComboBox->currentIndex());
containerRuntime =
containerRuntimeComboBox->itemText(containerRuntimeComboBox->currentIndex());
cpus = cpuField.text().toInt();
memory = memoryField.text().toInt();
QStringList args = { "start",
"-p",
profile,
"--driver",
driver,
"--container-runtime",
containerRuntime,
"--cpus",
QString::number(cpus),
"--memory",
QString::number(memory) };
sendMinikubeCommand(args);
}
}
void Window::initMachine()
{
askName();
updateClusters();
}
void Window::sshConsole()
{
QString program = minikubePath();
#ifndef QT_NO_TERMWIDGET
QMainWindow *mainWindow = new QMainWindow();
int startnow = 0; // set shell program first
QTermWidget *console = new QTermWidget(startnow);
QFont font = QApplication::font();
font.setFamily("Monospace");
font.setPointSize(10);
console->setTerminalFont(font);
console->setColorScheme("Tango");
console->setShellProgram(program);
QStringList args = { "ssh" };
console->setArgs(args);
console->startShellProgram();
QObject::connect(console, SIGNAL(finished()), mainWindow, SLOT(close()));
mainWindow->setWindowTitle(nameLabel->text());
mainWindow->resize(800, 400);
mainWindow->setCentralWidget(console);
mainWindow->show();
#else
QString terminal = qEnvironmentVariable("TERMINAL");
if (terminal.isEmpty()) {
terminal = "x-terminal-emulator";
if (QStandardPaths::findExecutable(terminal).isEmpty()) {
terminal = "xterm";
}
}
QStringList arguments = { "-e", QString("%1 ssh -p %2").arg(program, selectedCluster()) };
QProcess *process = new QProcess(this);
process->start(QStandardPaths::findExecutable(terminal), arguments);
#endif
}
void Window::dashboardBrowser()
{
dashboardClose();
QString program = minikubePath();
QProcess *process = new QProcess(this);
QStringList arguments = { "dashboard", "-p", selectedCluster() };
process->start(program, arguments);
dashboardProcess = process;
dashboardProcess->waitForStarted();
}
void Window::dashboardClose()
{
if (dashboardProcess) {
dashboardProcess->terminate();
dashboardProcess->waitForFinished();
}
}
#endif

146
gui/window.h Normal file
View File

@ -0,0 +1,146 @@
/****************************************************************************
**
** Copyright 2022 The Kubernetes Authors All rights reserved.
**
** Copyright (C) 2021 Anders F Björklund
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef WINDOW_H
#define WINDOW_H
#include <QSystemTrayIcon>
#ifndef QT_NO_SYSTEMTRAYICON
#include <QDialog>
QT_BEGIN_NAMESPACE
class QAction;
class QCheckBox;
class QComboBox;
class QGroupBox;
class QIcon;
class QLabel;
class QLineEdit;
class QMenu;
class QPushButton;
class QSpinBox;
class QTextEdit;
class QTableView;
class QProcess;
QT_END_NAMESPACE
#include "cluster.h"
//! [0]
class Window : public QDialog
{
Q_OBJECT
public:
Window();
void setVisible(bool visible) override;
protected:
void closeEvent(QCloseEvent *event) override;
private slots:
void messageClicked();
void updateButtons();
void dashboardClose();
private:
void createActionGroupBox();
void createActions();
void createTrayIcon();
void startMinikube();
void stopMinikube();
void deleteMinikube();
ClusterList getClusters();
QString selectedCluster();
void setSelectedCluster(QString cluster);
QTableView *clusterListView;
void createClusterGroupBox();
QGroupBox *clusterGroupBox;
ClusterModel *clusterModel;
ClusterHash getClusterHash();
bool sendMinikubeCommand(QStringList cmds);
bool sendMinikubeCommand(QStringList cmds, QString &text);
void updateClusters();
void askCustom();
void askName();
QComboBox *driverComboBox;
QComboBox *containerRuntimeComboBox;
void initMachine();
void sshConsole();
void dashboardBrowser();
QPushButton *sshButton;
QPushButton *dashboardButton;
QProcess *dashboardProcess;
QPushButton *startButton;
QPushButton *stopButton;
QPushButton *deleteButton;
QPushButton *refreshButton;
QPushButton *createButton;
QAction *minimizeAction;
QAction *restoreAction;
QAction *quitAction;
QSystemTrayIcon *trayIcon;
QMenu *trayIconMenu;
QIcon *trayIconIcon;
};
//! [0]
#endif // QT_NO_SYSTEMTRAYICON
#endif

View File

@ -0,0 +1,79 @@
---
title: "Setting Up minikube GUI"
linkTitle: "Setting Up minikube GUI"
weight: 1
date: 2022-02-25
---
## Overview
- This guide will show you how to setup the minikube GUI
- **WARNING!** This GUI is a prototype and therefore may be unstable or contain bugs. Please use at your own risk, we are not responsible for damages.
## Before You Begin
- You will need to already have minikube setup on your machine, follow the [Getting Start doc]({{< ref "/docs/commands/start" >}}) if not already done.
## Steps
{{% tabs %}}
{{% tab macOS %}}
1. Download the zipped folder
```shell
curl -LO https://storage.googleapis.com/minikube-gui/v0.0.1/minikube-gui-mac.zip
```
2. Unzip
```shell
unzip minikube-gui-mac.zip
```
3. Open the application
```shell
open dist/systray.app
```
4. If you see the following, click cancel.
![Mac unverified developer](/images/gui/mac.png)
5. Open System Preferences and go to Security & Privacy -> General and click "Open Anyway".
{{% /tab %}}
{{% tab Windows %}}
1. Download the zipped folder via PowerShell (below) or via your [browser](https://storage.googleapis.com/minikube-gui/v0.0.1/minikube-gui-windows.zip) (faster)
```shell
Invoke-WebRequest -Uri 'https://storage.googleapis.com/minikube-gui/v0.0.1/minikube-gui-windows.zip' -UseBasicParsing
```
2. Unzip
```shell
Expand-Archive minikube-gui-windows.zip
```
3. Open the application
```shell
.\minikube-gui-windows\dist\systray.exe
```
4. If you see the following, click `More info` and then `Run anyway`
![Windows unreconized app](/images/gui/windows.png)
{{% /tab %}}
{{% tab Linux %}}
1. Download the zipped folder
```shell
curl -LO https://storage.googleapis.com/minikube-gui/v0.0.1/minikube-gui-linux.zip
```
2. Unzip
```shell
unzip minikube-gui-linux.zip
```
3. Open the application
```shell
dist/systray
```
{{% /tab %}}
{{% /tabs %}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB