- SpreadSheet (Functionality)
- Text Editor
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.
- How to install Qt
- Clone the repo
git clone https://github.com/IlyasKadi/SpreadSheet.git
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 |
---|---|---|
options | help |
---|---|
We did add the following modifications:
- 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.
- 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);
}
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:
-
Create a Form Class:
-
Using the designer obtain the following the form:
Ui components of the Go Dialog.
- 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));
- 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:
- First we will create the proper slot called goCellSlot to respond to the action trigger.
private slots:
void goCellSlot(); // Go to a Cell slot
- 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);
- 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 |
---|---|
Go to Cell dialog and response.
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.
- Create a Form Class with the following ui:
Find Dialog ui form.
- Add a Getter to obtain the searched text.
- 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 aQString
. -
Finally, the method to change the focused cell is
spreadsheet->setCurrentCell(int i, int j);
String to find | String found |
---|---|
Find Dialog ui form.
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
.
.
.
We will start by writing a private function saveContent(QSTring filename)
to save the content of our spreadsheet in a text file.
- We will add the declaration in the header file:
protected:
void saveContent(QString filename)const;
- 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.
- 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();
}
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);
}
}
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);
}
}
}
void SpreadSheet::copyslot()
{
auto cell = spreadsheet->item(spreadsheet->currentRow(),spreadsheet>currentColumn());
QString cellc =cell->text();
clipboard->setText(cellc);
}
void SpreadSheet::cutslot()
{
auto cell = spreadsheet->item(spreadsheet->currentRow(),spreadsheet->currentColumn());
QString cellc =cell->text();
clipboard->setText(cellc);
cell->setText("");
}
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);
}
void SpreadSheet::deleteslot()
{
int selcol = spreadsheet->currentColumn();
int selrow = spreadsheet->currentRow();
auto cell = spreadsheet->item(selrow,selcol);
cell->setText("");
}
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);
}
}
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);
}
}
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 |
---|---|
The last file is always shown in the top of the 5 recent files (no redundancy, and only max of 5 files)
void SpreadSheet::openrecentfilesslot()
{
auto action = dynamic_cast<QAction*>(sender());
if(action)
{
QString path = action->text();
currentFile=new QString(path);
loadContent(path);
setWindowTitle(path);
}
}
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;
}
}
}
void SpreadSheet::aboutqtslot()
{
QMessageBox::aboutQt(this,"about_QT");
}
void SpreadSheet::aboutslot()
{
QMessageBox::about(this,"About spreadsheet",abouttext);
}
void SpreadSheet::saveasslot()
{
if(currentFile)
{
QFileDialog D;
auto filename =D.getSaveFileName();
currentFile=new QString(filename);
setWindowTitle(*currentFile);
saveContent(*currentFile);
}
}
void SpreadSheet::newfileslot()
{
auto newfile = new SpreadSheet;
newfile->show();
}
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.
First create a new project called TextEditor
using the following steps:
- Choose a
QT Widgets Application
. - Name your project
TEXT_editor
- Inherit now from QMainWindow.
- Choose all the remaining default choices.
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 |
---|---|---|
Set of menus for our application.
void TextEditor::on_actionNew_triggered()
{
auto newfile = new TextEditor;
newfile->show();
}
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);
}
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);
}
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
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 |
---|---|---|
Once you select a test copy,cut are enabled
void TextEditor::on_action_About_triggered()
{
QMessageBox::about(this,"About TextEditor",text);
}
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
Amazing work and Dedication. Completely worth the delay :)