/SpreadSheet

Create a MainWindow based application using the designer

Primary LanguageC++


Logo

Application Using Main Window

Create a MainWindow based application using the designer

Table of Contents
  1. SpreadSheet (Functionality)
  2. Text Editor

About The Project

Getting Started

This is an example of how you may give instructions on setting up your project locally. To get a local copy up and running follow these simple example steps.

  1. How to install Qt
  2. Clone the repo
    git clone https://github.com/IlyasKadi/SpreadSheet.git

(back to top)

SpreadSheet

Context

In the lecture on QMainWindow, we wrote the code for the graphical and set of actions for our main SpreadSeet application. Now we will focus on writing a set of basic functionality.

In the our last iteration of the SpreadSheet we did obtain an application with:

  • Menu Bar
  • Two tools bars
  • Status bar to print the informations

You should have an application that looks like that :

File Edit tools
Image Image Image
options help
Image Image

(back to top)

Remarks

We did add the following modifications:

  1. The updateStatusBar now takes two ints in order to syncrhonize with the selected item from the spreadsheet.
 void updateStatusBar(int, int) 

Here is the implementation of this function:

   void SpreadSheet::updateStatusBar(int row, int col)
    {
     QString cell{"(%0, %1)"};
     cellLocation->setText(cell.arg(row+1).arg(col+1));
    }

Which simply change the cellLocation text with the current cell coordinates.

  1. We added the makeConnexion() function to connect all the actions. Here is the content of the this function:
 void SpreadSheet::makeConnexions()
{
    // --------- Connexion for the  select all action ----/
    connect(all, &QAction::triggered,
            spreadsheet, &QTableWidget::selectAll);

    // Connection for the  show grid
    connect(showGrid, &QAction::triggered,
            spreadsheet, &QTableWidget::setShowGrid);

    //Connection for the exit button
    connect(exit, &QAction::triggered, this, &SpreadSheet::close);

    //connectting the chane of any element in the spreadsheet with the update status bar
    connect(spreadsheet, &QTableWidget::cellClicked, this,  &SpreadSheet::updateStatusBar);

    //connnet the gocell action
    connect(goCell, &QAction::triggered, this, &SpreadSheet::goCellSlot);

    //connnet the find action
    connect(find, &QAction::triggered, this, &SpreadSheet::findSlot);

    //connnet the savefile action
    connect(save,&QAction::triggered,this,&SpreadSheet::saveSlot);

    //connnet the saveAsfile action
    connect(saveAs,&QAction::triggered,this,&SpreadSheet::saveasslot);

    //connnet the newfile action
    connect(newFile,&QAction::triggered,this,&SpreadSheet::newfileslot);

    //connect the openfile action
    connect(open,&QAction::triggered,this,&SpreadSheet::loadslot);

    //connect the copy action
    connect(copy,&QAction::triggered,this,&SpreadSheet::copyslot);

    //connect the cut action
    connect(cut,&QAction::triggered,this,&SpreadSheet::cutslot);

    //connect the paste action
    connect(paste,&QAction::triggered,this,&SpreadSheet::pasteslot);

    //connect the delete action
    connect(deleteAction,&QAction::triggered,this,&SpreadSheet::deleteslot);

    //connect the select row
    connect(row,&QAction::triggered,this,&SpreadSheet::selectrowslot);

    //connect the select col
    connect(Column,&QAction::triggered,this,&SpreadSheet::selectrcolslot);

    //connect the about-Qt
    connect(aboutQt,&QAction::triggered,this,&SpreadSheet::aboutqtslot);

    //connect the about
    connect(about,&QAction::triggered,this,&SpreadSheet::aboutslot);
}

(back to top)

Go-Cell

Now we will add the function for the goCell action. For that, we need to create a Dialog for the user to select a cell.

here are the steps to acomplish that:

  1. Create a Form Class:

  2. Using the designer obtain the following the form:

Ui components of the Go Dialog.

  1. Add the regular expression validator for the lineEdit:
//Validating the regular expression
QRegExp regCell{"[A-Z][1-9][0-9]{0,2}"};
//Validating the regular expression
ui->lineEdit->setValidator(new QRegExpValidator(regCell));
  1. Add a public Getter for the line edit Text to get the cell address:
   QString GoCellDialog::cell() const
   {
       return ui->lineEdit->text();
   }

No we are setup to create the interesting connexion between the goCell action:

  1. First we will create the proper slot called goCellSlot to respond to the action trigger.
   private slots:
   void goCellSlot();            // Go to a Cell slot
  1. connect the action to its proper slot in the makeConnexions function:
   //Connextion between the gocell action and the gocell slot
   connect(goCell, &QAction::triggered, this, &SpreadSheet::goCellSlot);
  1. Now for the fun part. We will implement the goCellSlot() function:
   void SpreadSheet::goCellSlot()
     {
        //Creating the dialog
        GoCellDialog D;
        //Executing the dialog and storing the user response
        auto reply = D.exec();
       //Checking if the dialog is accepted
       if(reply == GoCellDialog::Accepted)
       {
             //Getting the cell text
             auto cell = D.cell();
             //letter distance
             int row = cell[0].toLatin1() - 'A';
             cell.remove(0,1);
             //second coordinate
             int col =  cell.toInt();
             //changing the current cell
             spreadsheet->setCurrentCell(row, col-1);
         }
     }
Cell location Cell avtivated
Image Image

Go to Cell dialog and response.

(back to top)

Find Dialog

We will move now for the Find dialog. This dialog prompts the user for a input and seek a cell that contains the entered text.

  1. Create a Form Class with the following ui:

Find Dialog ui form.

  1. Add a Getter to obtain the searched text.
  2. Implements the connexion between the dialog and the find function:

Here is a set of useful information about the QTableWidget class:

  • The method rowCount() gives the number of rows.

  • The method columnCount() gives the number of columns in the spreadsheet.

  • The Method item(int i, int j) return a pointer on the cell indexed by i and j.

  • If this pointer is not null, you could get its content by the method text() which returns a QString.

  • Finally, the method to change the focused cell is

   spreadsheet->setCurrentCell(int i, int j);
String to find String found
Image Image

Find Dialog ui form.

(back to top)

Saving-and-loading-files

For our final task, we will learn how to save the content of our spreadsheet in a simple format. Since a spreadsheet is not forcefully tabular, it will be a waste to save all the empty data. Hence, we will choose a simple format that store the coordinates and the content of the non empty cells.

i1, j1, content1
i2, j2, content2
.
.
.

(back to top)

Saving-Content

We will start by writing a private function saveContent(QSTring filename) to save the content of our spreadsheet in a text file.

  1. We will add the declaration in the header file:
 protected:
     void saveContent(QString filename)const;
  1. For the implementation, we will using two classes:
  • QFile which provides an interface to read and write in files.
  • QTextStream for manipulating objects with a stream such as a file.
  1. Here is the complete implementation of this function:
void SpreadSheet::saveContent(QString filename) const
 {

     //Gettign a pointer on the file
     QFile file(filename);

     //Openign the file
     if(file.open(QIODevice::WriteOnly))  //Opening the file in writing mode
     {
         //Initiating a stream using the file
         QTextStream out(&file);

         //loop to save all the content
         for(int i=0; i < spreadsheet->rowCount();i++)
             for(int j=0; j < spreadsheet->columnCount(); j++)
             {
                 auto cell = spreadsheet->item(i, j);

                 //Cecking if the cell is non empty
                 if(cell)
                 out << cell->row() << ", "<< cell->column() << ", " << cell->text() << endl;
             }

     }
     file.close();
 }

(back to top)

Save-File-action

Now that we have an operational saveContent function, we could focus on the slot itself.

So first we will create a slot to respond to the action trigger in the header.

private slots:
    void saveSlot();             //Slot to save the content of the file

Now we will add the connexion in the makeConnexion function:

   //Connexion for the saveFile
   connect(save, &QAction::triggered, this, &SpreadSheet::saveSlot);

Finally for the interesting part, the implementation of the slot

void SpreadSheet::saveSlot()
{
    //Creating a file dialog to choose a file graphically
    auto dialog = new QFileDialog(this);

    //Check if the current file has a name or not
    if(currentFile == "")
    {
       currentFile = dialog->getSaveFileName(this,"choose your file");

       //Update the window title with the file name
       setWindowTitle(currentFile);
    }

   //If we have a name simply save the content
   if( currentFile != "")
   {
           saveContent(currentFile);
   }
}

(back to top)

Load-File

void SpreadSheet::loadContent(QString filename)
{

    for(int i=0;i<spreadsheet->rowCount();i++)
    {
        for(int j=0;j<spreadsheet->columnCount();j++)
        {
            auto cell = spreadsheet->item(i,j);
            if(cell)
            {
                cell->setText("");
            }
        }
    }
    QFile file(filename);

    if(file.open(QIODevice::ReadOnly))
    {
        QTextStream in(&file);
        while(!in.atEnd())
        {
            QString line;
            line=in.readLine();
            auto tokens=line.split(QChar(','));
            int row = tokens[0].toInt();
            int col = tokens[1].toInt();
            auto cell = new QTableWidgetItem(tokens[2]);
           spreadsheet->setItem(row,col,cell);
        }
    }
}

(back to top)

Other-Actions's slots

Copy

void SpreadSheet::copyslot()
{
    auto cell = spreadsheet->item(spreadsheet->currentRow(),spreadsheet>currentColumn());
    QString cellc =cell->text();
    clipboard->setText(cellc);

}

Cut

void SpreadSheet::cutslot()
{
     auto cell = spreadsheet->item(spreadsheet->currentRow(),spreadsheet->currentColumn());
     QString cellc =cell->text();
     clipboard->setText(cellc);
     cell->setText("");

}

Paste

void SpreadSheet::pasteslot()
{
   QString text = clipboard->text();
   auto  cell = spreadsheet->item(spreadsheet->currentRow(),spreadsheet->currentColumn());

     if(!cell)
     {
         cell = new QTableWidgetItem((text));
         spreadsheet->setItem(spreadsheet->currentRow(),spreadsheet->currentColumn(),cell);
     }


    cell->setText(text);
}

Delete

void SpreadSheet::deleteslot()
{
    int selcol = spreadsheet->currentColumn();
    int selrow = spreadsheet->currentRow();
    auto cell = spreadsheet->item(selrow,selcol);
    cell->setText("");
}

Select-rows

void SpreadSheet::selectrowslot()
{
  int row = spreadsheet->currentRow();
    for(int i=0;i<spreadsheet->columnCount();i++)
    {
        auto  cell = spreadsheet->item(row,i);
        cell =new QTableWidgetItem;
        spreadsheet->setItem(row,i,cell);
        cell->setSelected(true);
    }
}

Select-columns

void SpreadSheet::selectrcolslot()
{
  int col = spreadsheet->currentColumn();
    for(int i=0;i<spreadsheet->rowCount();i++)
    {
        auto  cell = spreadsheet->item(i,col);
        cell =new QTableWidgetItem;
        spreadsheet->setItem(i,col,cell);
        cell->setSelected(true);
    }
}

Show-last-5-recent-files

void SpreadSheet::saveContent(QString filename)
{
  QFile file(filename);

  if(file.open(QIODevice::WriteOnly))
  {
      QTextStream out(&file);
      for(int i=0;i<spreadsheet->rowCount();i++)
      {
          for(int j=0;j<spreadsheet->columnCount();j++)
          {
              auto cell = spreadsheet->item(i,j);
              if(cell)
              {
                  out  << cell->text() << ",";
              }

          }
           out<< Qt::endl;
      }
  }

  if(files.length()==5)
  {
      files.pop_back();
      files.push_front(new QAction(filename));
      recentfile->clear();
      recentfile->addActions(files);
      for(int i=0; i <files.size(); i++)
          connect(files[i],&QAction::triggered,this,&SpreadSheet::openrecentfilesslot);
  }

   if(files.length()>=0&&files.length()<5)
  {
       int c=0;
      for(int i=0;i<files.length();i++)
      {
          if(filename==files[i]->text())
          {
              c++;
          }


      }
      if(c==0)
      {
          files.push_front(new QAction(filename));

      }

   }
   recentfile->clear();
   recentfile->addActions(files);
   for(int i=0; i <files.size(); i++)
       connect(files[i],&QAction::triggered,this,&SpreadSheet::openrecentfilesslot);



  file.close();

}
File Edit
Image Image

The last file is always shown in the top of the 5 recent files (no redundancy, and only max of 5 files)

Open-recent-files

void SpreadSheet::openrecentfilesslot()
{
   auto action = dynamic_cast<QAction*>(sender());
   if(action)
   {
       QString path = action->text();
       currentFile=new QString(path);
       loadContent(path);
        setWindowTitle(path);
   }
}

Open-and-save-csv-files

void SpreadSheet::loadContent(QString filename)
{

   QFile file(filename);

   if(file.open(QIODevice::ReadOnly))
   {
       QTextStream in(&file);
       int row=0;
       while(!in.atEnd())
       {
           int col=0;
           QString line;
           line=in.readLine();
            auto tokens=line.split(QChar(','));
            for(QString s:tokens)
            {
                auto cell = new QTableWidgetItem(s);
                spreadsheet->setItem(row,col,cell);
                col++;
            }
            row++;
       }
   }
}

void SpreadSheet::saveContent(QString filename)
{
   QFile file(filename);

   if(file.open(QIODevice::WriteOnly))
   {
       QTextStream out(&file);
       for(int i=0;i<spreadsheet->rowCount();i++)
       {
           for(int j=0;j<spreadsheet->columnCount();j++)
           {
               auto cell = spreadsheet->item(i,j);
               if(cell)
               {
                   out  << cell->text() << ",";
               }

           }
            out<< Qt::endl;
       }
   }
}

About-Qt

void SpreadSheet::aboutqtslot()
{
    QMessageBox::aboutQt(this,"about_QT");
}

About-spreadsheet

void SpreadSheet::aboutslot()
{
    QMessageBox::about(this,"About spreadsheet",abouttext);
}

Save-As

void SpreadSheet::saveasslot()
{
    if(currentFile)
    {
        QFileDialog D;
        auto filename =D.getSaveFileName();
        currentFile=new QString(filename);
        setWindowTitle(*currentFile);
        saveContent(*currentFile);
    }
}

New-file

void SpreadSheet::newfileslot()
{
    auto newfile = new SpreadSheet;
    newfile->show();
}

(back to top)

Text-Editor

For your first example, we will playing the Designer for a fast application creation. The application is from Qt Examples and is a simple text editor program built around QPlainText.

Example for the main text editor.

(back to top)

Creating-the-project

First create a new project called TextEditor using the following steps:

  1. Choose a QT Widgets Application.
  2. Name your project TEXT_editor
  3. Inherit now from QMainWindow.
  4. Choose all the remaining default choices.

Menus

We will mainly use the designer for a rapid design of it features. But if you feel adventurous you can write all in using code.

Here is an overview of the menus:

File Edit Help
Image haikyuu Image haikyuu Image haikyuu

Set of menus for our application.

(back to top)

Actions

File-Actions

new
void TextEditor::on_actionNew_triggered()
{
    auto newfile = new TextEditor;
    newfile->show();

}
open
void TextEditor::loadContent(QString filename)
{
    QFile file(filename);

    if(file.open(QIODevice::ReadOnly))
    {
           QTextStream in(&file);
           QString text;

           while(!in.atEnd())
           {
               QString line;
               line=in.readLine();
               text+=line;
               text+="\n";
           }

           ui->plainTextEdit->setPlainText(text);
    }
}
void TextEditor::on_actionOpen_triggered()
{
    QFileDialog d;
    auto filen = d.getOpenFileName();
    currentFile=new QString(filen);
    setWindowTitle(*currentFile);
    loadContent(filen);

}
save
void TextEditor::saveContent(QString filename)
{
    QFile file(filename);

    if(file.open(QIODevice::WriteOnly))
    {
        QTextStream out(&file);

                    out  << ui->plainTextEdit->toPlainText();
        }

    file.close();
}
void TextEditor::on_actionSave_triggered()
{
    if(!currentFile)
    {
        QFileDialog D;
        auto filename =D.getSaveFileName();
        currentFile=new QString(filename);
        setWindowTitle(*currentFile);
    }
     saveContent(*currentFile);

}
saveAs
void TextEditor::on_actionSave_As_triggered()
{

    if(currentFile)
    {
        QFileDialog D;
        auto filename =D.getSaveFileName();
        currentFile=new QString(filename);
        setWindowTitle(*currentFile);
        saveContent(*currentFile);
    }

}

The exit action is created within the designer form

Edit-Actions

For the copy,cut and paste actions are created with the designer form

void TextEditor::textSelected(bool isselected)
{
    if (isselected){
        ui->actionCut->setEnabled(true);
        ui->actionCopy->setEnabled(true);
   }
}
before selecting text After selecting a text After selecting a text
Image haikyuu Image haikyuu Image haikyuu

Once you select a test copy,cut are enabled

Help-Actions

About_T-E
void TextEditor::on_action_About_triggered()
{
    QMessageBox::about(this,"About TextEditor",text);

}
AboutQt
void TextEditor::on_actionAbout_Qt_triggered()
{
    QMessageBox::aboutQt(this,"about_QT");

}

Out Team - AIT EL KADI Ilyas - AZIZ Oussama

Project Link: https://github.com/IlyasKadi/SpreadSheet

Encadré par : Mr.Belcaid.anass

(back to top)

Amazing work and Dedication. Completely worth the delay :)