Haruno19/starfetch

qt GUI

Closed this issue · 3 comments

su8 commented

qt6/5 gui for starfetch, still in works

For now, only "random" and "help" is working. Still figuring out how to display HTML instead 033 ansi color text.

mainwindow.cpp

#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <ctime>
#include <filesystem>
#include <regex>

#include <QApplication>
#include <QCompleter>
#include <QStringList>
#include <QColor>
#include <QPalette>
#include <QAbstractItemView>
#include <QRandomGenerator>
#include <QIcon>

#include "include/json.hpp"
#include "mainwindow.h"
#include "./ui_mainwindow.h"

using namespace std;
using json = nlohmann::json;

static inline void PrintConst(string &pathc);  //formats the template file with the requested data and prints out the constellation info
static string RandomConst();   //select a random constellation from the available ones
static inline void PrintList();   //prints out the list of the available constellations
static void Error(string &err, int type);   //shows an error message
static void Help();    //prints out the help message

/*#ifdef __APPLE__    //selection the right working path based on the OS type
    string path = "/usr/local/opt/starfetch/res/";
#else
    string path = "/usr/local/starfetch/res/";
#endif*/

#ifdef _WIN32
  string path = "C:\\starfetch\\";
  string SEP = "\\";
#else
  string path = "/usr/local/share/starfetch/";
  string SEP = "/";
#endif // _WIN32

static QString oldText = "";
static QStringList wordList = {
    "help", "random", "", "", "",
    "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", ""
};
static QCompleter *completer = new QCompleter(wordList, nullptr);

Ui::MainWindow *UI;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    UI = ui;

    QPalette p = ui->textEdit->palette();
    p.setColor(QPalette::Base, Qt::black);
    p.setColor(QPalette::Text, Qt::green);
    ui->textEdit->setPalette(p);

    ui->textEdit->setText(static_cast<QString>("Type 'help' to see the available constellations"));

    ui->lineEdit->setCompleter(completer);
    completer->popup()->setStyleSheet("background-color:rgb(54, 57, 63); color:white;");

    ui->lineEdit->setClearButtonEnabled(true);
    ui->lineEdit->addAction(static_cast<QIcon>("media/linux.png"), QLineEdit::LeadingPosition);

    connect(ui->lineEdit, &QLineEdit::returnPressed, this, &MainWindow::on_pushButton_clicked);
}

MainWindow::~MainWindow()
{
    delete completer;
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    std::string pathc = path;    //path to the constellation file
    std::string userInput = static_cast<std::string>(ui->lineEdit->text().toStdString());
    if (userInput.empty())
    {
        return;
    }

    if(userInput == "random")   //if there's no additional arguments
    {
        pathc += RandomConst(); //selects a random constellation
    }

    else if (userInput == "h" || userInput == "help")
    {
        Help();
        return;
    }

    else if (userInput.find("select ") != std::string::npos)
    {
        pathc += "constellations" + SEP; //updating the path to the constellations folder
        string subStr = std::regex_replace(userInput, std::regex("select "), "");
        pathc += subStr;   //adding the name of the requested constellation to the path
        pathc += ".json";
    }

    else if (userInput == "l" || userInput == "list")
    {
        PrintList();
        return;
    }

    else
    {
        Error(userInput, 1);  //if the reqeusted option isn't recognized, an error occours
        return;
    }

    PrintConst(pathc);  //prints the constellation

    ui->lineEdit->setText(static_cast<QString>(""));
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

static inline void PrintConst(string &pathc)
{
    ifstream c(pathc);  //opens the file containing constellation info
    ifstream f(path+"template");    //opens the output template file
    stringstream strStream;
    string s, l;
    json j;

    if(f.is_open())
    {
        strStream << f.rdbuf(); //read the template
        s = strStream.str();    //converts it to a string
        replace(s.begin(), s.end(), '^', '\033');   //replace '^' with the '\e' to print bold/colored text
        f.close();  //closes the template
    }

    if(c.is_open())
    {
        c >> j;     //parse the selected JSON file
        //fills the template with dats
        s.replace(s.find("%0"), string("%0").size(), j["title"].get<string>());
        s.replace(s.find("%11"), string("%11").size(), j["name"].get<string>());
        s.replace(s.find("%12"), string("%12").size(), j["quadrant"].get<string>());
        s.replace(s.find("%13"), string("%13").size(), j["right ascension"].get<string>());
        s.replace(s.find("%14"), string("%14").size(), j["declination"].get<string>());
        s.replace(s.find("%15"), string("%15").size(), j["area"].get<string>());
        s.replace(s.find("%16"), string("%16").size(), j["main stars"].get<string>());

        //renders the constellation's graph from the coordinates specified in the JSON file
        for(int i=1;i<=10;i++)  //for each of the lines (10)
        {
            l="";
            for(int k=1;k<=22;k++)  //for each of the columns of the graph (22)
                //if the JSON file specifies a star at position k
                if(j["graph"]["line"+to_string(i)].find(to_string(k)) != j["graph"]["line"+to_string(i)].end())
                    l+=j["graph"]["line"+to_string(i)][to_string(k)].get<string>(); //put the star (which is stored into the JSON fine, might change this in the future)
                else
                    l+=" "; //put a space

            //insert the line into the template
            s.replace(s.find("%"+to_string(i)), string("%"+to_string(i)).size(), l);
        }

        c.close();
        UI->textEdit->setText(static_cast<QString>(s.c_str()));  //prints the output
    }

    else
    {
        //Error(static_cast<string>(""), 2);
    }
}

static string RandomConst()
{
    size_t pos;
    string s;

    //SHOULD BE IMPROVED IN THE FUTURE
    //gets every constellation name in the "constellation/" directory, and exits when two randomly generated numbers are equal, resulting in picking a random file
    for (const auto & entry : filesystem::directory_iterator(path+"constellations" + SEP))
    {
        pos = entry.path().u8string().find("constellations" + SEP);
        s = entry.path().u8string().substr(pos);
        if(s != "constellations/.DS_Store" && QRandomGenerator::global()->bounded(0, 11) == QRandomGenerator::global()->bounded(0, 11))
            break;
    }

    return s;
}

static inline void PrintList()
{
    string s;
    oldText = "";
    QString outStr = static_cast<string>("✦ \e[1;37mavailable constellations\e[0m: ").c_str();

    UI->textEdit->setText(outStr + oldText + static_cast<QString>("\n"));

    //prints every constellation name from the files name in the "constellations/" directory
    for (const auto & entry : filesystem::directory_iterator(path+"constellations" + SEP))
    {
        s = entry.path().u8string().substr(entry.path().u8string().find("constellations" + SEP)+15); //from "/usr/local/opt/starfetch/res/constellations/xxxxxx" to "xxxxxx"
        s = s.substr(0, s.length()-5);
        if(s != ".DS_")
        {
            oldText += static_cast<QString>(s.c_str()) + static_cast<QString>("\n");
            UI->textEdit->setText(outStr + oldText);
        }
    }
    oldText = "";
}

static void Error(string &err, int code)
{
    switch(code)    //each error has a specific code
    {
        case 0: //0 for the missing input
            UI->textEdit->setText(static_cast<QString>("Error: you must input a constellation name after -n."));
            break;
        case 1: //1 for the invalid argument
            UI->textEdit->setText(static_cast<QString>(static_cast<string>("Error: '" + err + "' isn't a valid argument.").c_str()));
            break;
        case 2: //2 for the invalid constellation name
            UI->textEdit->setText(static_cast<QString>("Error: the constellation you asked for isn't recognized."));
            break;
    }

    Help(); //after any error occours, the help message is shown
}

static void Help()
{
    std::ostringstream ss;
    ifstream f(path + "help_message.txt");
    ss << f.rdbuf();
    string str = ss.str();
    UI->textEdit->setText(static_cast<QString>(str.c_str()));
    f.close();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(starfetch VERSION 0.1 LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_THREAD_LIBS_INIT "-lpthread")
set(CMAKE_HAVE_THREADS_LIBRARY 1)
set(CMAKE_USE_WIN32_THREADS_INIT 0)
set(CMAKE_USE_PTHREADS_INIT 1)
set(THREADS_PREFER_PTHREAD_FLAG ON)

find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)

set(PROJECT_SOURCES
        mainwindow.cpp
        mainwindow.h
        mainwindow.ui
)

if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
    qt_add_executable(starfetch
        MANUAL_FINALIZATION
        ${PROJECT_SOURCES}
    )
else()
    if(ANDROID)
        add_library(starfetch SHARED
            ${PROJECT_SOURCES}
        )
    else()
        add_executable(starfetch
            ${PROJECT_SOURCES}
        )
    endif()
endif()

target_link_libraries(starfetch PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)

set_target_properties(starfetch PROPERTIES
    MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

install(TARGETS starfetch
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

if(QT_VERSION_MAJOR EQUAL 6)
    qt_finalize_executable(starfetch)
endif()

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>hackzyGUI</string>
  </property>
  <property name="windowIcon">
   <iconset>
    <normaloff>media/icon.xpm</normaloff>media/icon.xpm</iconset>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QLineEdit" name="lineEdit">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
      <width>411</width>
      <height>41</height>
     </rect>
    </property>
    <property name="placeholderText">
     <string extracomment="Type here">Ask me anything...</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>412</x>
      <y>0</y>
      <width>391</width>
      <height>41</height>
     </rect>
    </property>
    <property name="text">
     <string>Okay</string>
    </property>
    <property name="autoDefault">
     <bool>true</bool>
    </property>
    <property name="default">
     <bool>false</bool>
    </property>
   </widget>
   <widget class="QTextEdit" name="textEdit">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>40</y>
      <width>811</width>
      <height>571</height>
     </rect>
    </property>
    <property name="readOnly">
     <bool>true</bool>
    </property>
   </widget>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

media/icon.xpm

/* XPM */
static char *pdf2img_c_icon_xpm[] = {"24 24 124 2","     c None",".    c #484848","+     c #505050","@     c #818181","#     c #4E4E4E","$     c #484848","%     c #505050","&     c #3D3D3E","*     c #494949","=     c #808080","-     c #575757",";     c #383839",">     c #828282",",     c #4E4E4E","'     c #494949",")     c #464646","!     c #4A4A4A","~     c #4C4C4C","{     c #505050","]     c #4E4E4E","^     c #F9C940","/     c #4B4B4B","(     c #F8C73D","_     c #494949",":     c #4D4D4D","<     c #515151","[     c #525252","}     c #535353","|     c #818181","1     c #545454","2     c #FACA42","3     c #F8C73E","4     c #F8C83E","5     c #F7C63C","6     c #58554C","7     c #373838","8     c #4F4F4F","9     c #59564D","0     c #484848","a     c #383839","b     c #373738","c     c #3F3F3F","d     c #39393A","e     c #F6C335","f     c #FACB43","g     c #565656","h     c #F6C438","i     c #8E7D49","j     c #575757","k     c #8D7B47","l     c #C2A041","m     c #363637","n     c #F7C73C","o     c #383838","p     c #8D7B48","q     c #404141","r     c #FCCE48","s     c #FBCC45","t     c #474747","u     c #F6C333","v     c #C4A346","w     c #828282","x     c #907F4D","y     c #F2C749","z     c #FCCD47","A     c #FBCC44","B     c #363738","C     c #F9C93F","D     c #F9CA40","E     c #3A3B3B","F     c #F7C63B","G     c #EBB82A","H     c #8B763A","I     c #F7C53B","J     c #F6C232","K     c #6F664D","L     c #DBB547","M     c #F9CA41","N     c #F8C83F","O     c #F7C53A","P     c #F6C437","Q     c #D6AC36","R     c #6B6145","S     c #C3A243","T     c #C2A142","U     c #8D7A46","V     c #F6C43A","W     c #F6C439","X     c #565249","Y     c #F6C029","Z     c #827349","`     c #F8C93F"," .    c #806F41","..    c #B89A44","+.    c #E2B63D","@.    c #E1B53B","#.    c #F6C12E","$.    c #B59334","%.    c #555555","&.    c #464646","*.    c #B58F25","=.    c #FDD04A","-.    c #3E3F3F",";.    c #615A49",">.    c #5E5742",",.    c #746438","'.    c #414142",").    c #BDA24F","!.    c #F5BD1C","~.    c #EAB41A","{.    c #7F6C36","].    c #FED24C","^.    c #766A47","/.    c #7F6E3D","(.    c #EAB621","_.    c #817247",":.    c #ECBE3D","<.    c #817146","[.    c #EDC03E","}.    c #B79841","|.    c #56534A","1.    c #56524A","2.    c #B6953A","3.    c #F5C029","  j g %.1 } } [ < { +     # ] : ~ ~ >           ","j g %.%.1 } [ [ < { { + # ] ] : ~ ~ w @         ","g %.%.%.1 } [ [ < { { { ] ] ] : ~ ~ w | @       ","%.%.%.1 } } [ [ { { { 8 ] ] ] : ~ ~ w | | @     ","1 1 1 } } [ [ < { { { 9 9 ] ] : ~ ~ w | | | @   ","} } } } [ [ [ { { { { p k ] : ~ ~ ~ | | | | | = ","} [ [ [ [ [ { { { { 8 S T ] : ~ ~ ~ '.E d 7 b m ","[ [ [ [ < { { { { 8 9 ` n 6 ~ ~ ~ / / q d o b B ","< < < { { { { { 8 ] i D ( U ~ ~ ~ / / ! c a 7 7 ","{ { { { { { { 8 ] ] v ^ ( l ~ ~ / / / ! _ -.a a ","+ { { { { ).].=.r s f ^ 3 F W u Y !.*.! _ _ c ; ","  + { 8 ] ] x y z A 2 C ( I h J G H ! _ _ _ *   ","  # ] ] ] ] ] K L f M N ( O P Q R ! _ _ _ _ .   ","# ] ] ] ] ] ] ] 6 2 ^ 3 5 V e X ! ! _ _ _ _ 0 . ","] ] ] ] ] : : ~ Z ^ 4 ( I h J  .! _ _ _ _ 0 0 0 ",": : : : : ~ ~ ~ ..4 ( +.@.e #.$._ _ _ _ 0 0 0 0 ","~ ~ ~ ~ ~ ~ ~ ~ [.5 }.|.1.2.3.(._ _ _ _ 0 0 0 0 ","~ ~ ~ ~ ~ ~ ~ _.:.<./ / ! ! /.~.{._ _ 0 0 0 0 t ","~ ~ ~ ~ / / / ^.;./ ! ! _ _ _ >.,._ 0 0 0 0 t t ","/ / / / / / / / ! ! ! _ _ _ _ _ _ 0 0 0 0 t t t ","/ / / / / ! ! ! _ _ _ _ _ _ _ 0 0 0 0 0 t t t t ","! ! ! ! ! _ _ _ _ _ _ _ _ _ 0 0 0 0 0 t t t t t ","_ _ _ _ _ _ _ _ _ _ _ * . 0 0 0 0 0 t t t t t &.","  _ _ _ _ _ _ _ _ _ .     . 0 0 0 t t t t t &.  "};

Oh, wow!
I'm not very familiar with graphics environments and Qt, but I understand this makes starfetch render the constellations in a separate windows, right? That's an incredible work!
I need to thank you for the commitment and effort your putting in starfetch! 🙏🏻

I think it would be great to have also a GUI version of starfetch in addition to the CLI one!

su8 commented

I understand this makes starfetch render the constellations in a separate windows, right?

It renders everything in one window, graphical one. :}

It renders everything in one window, graphical one. :}

That's even cooler! :)