<commit_before>/******************************************

	Author: Tiago Britto Lobão
	tiago.blobao@gmail.com
*/

/*
	Purpose: Control an integrated circuit
	Cirrus Logic - CS5490

	Used to measure electrical quantities

	MIT License

******************************************/

#include "CS5490.h"

/******* Init CS5490 *******/

//For Arduino & ESP8622
#if !(defined ARDUINO_NodeMCU_32S ) && !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(ARDUINO_Node32s)
	CS5490::CS5490(float mclk, int rx, int tx){
		this->MCLK = mclk;
		this->cSerial = new SoftwareSerial(rx,tx);
	}
//For ESP32 AND MEGA
#else
	CS5490::CS5490(float mclk){
		this->MCLK = mclk;
		this->cSerial = &Serial2;
	}
#endif

void CS5490::begin(int baudRate){
	cSerial->begin(baudRate);
}

/**************************************************************/
/*                     PRIVATE METHODS                        */
/**************************************************************/

/******* Write a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::write(int page, int address, long value){

	uint8_t checksum = 0;
	for(int i=0; i<3; i++)
		checksum += 0xFF - checksum;

	//Select page and address
	uint8_t buffer = (pageByte | (uint8_t)page);
	cSerial->write(buffer);
	buffer = (writeByte | (uint8_t)address);
	cSerial->write(buffer);

	//Send information
	for(int i=0; i<3 ; i++){
		data[i] = value & 0x000000FF;
		cSerial->write(this->data[i]);
		value >>= 8;
	}
	//Calculate and send checksum
	buffer = 0xFF - data[0] - data[1] - data[2];
	cSerial->write(buffer);
}

/******* Read a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::read(int page, int address){

	cSerial->flush();

	uint8_t buffer = (pageByte | (uint8_t)page);
	cSerial->write(buffer);
	buffer = (readByte | (uint8_t)address);
	cSerial->write(buffer);

	//Wait for 3 bytes to arrive
	while(cSerial->available() < 3);
	for(int i=0; i<3; i++){
		data[i] = cSerial->read();
	}
}

/******* Give an instruction by the serial communication *******/

void CS5490::instruct(int value){

	cSerial->flush();

	uint8_t buffer = (instructionByte | (uint8_t)value);
	cSerial->write(buffer);
}

/*
  Function: toDouble
  Transforms a 24 bit number to a double number for easy processing data

  Param:
  data[] => Array with size 3. Each uint8_t is an 8 byte number received from CS5490
  LSBpow => Expoent specified from datasheet of the less significant bit
  MSBoption => Information of most significant bit case. It can be only three values:
    MSBnull (1)  The MSB is a Don't Care bit
    MSBsigned (2) the MSB is a negative value, requiring a 2 complement conversion
    MSBunsigned (3) The MSB is a positive value, the default case.

*/
double CS5490::toDouble(int LSBpow, int MSBoption){

	uint32_t buffer = 0;
	double output = 0.0;
	bool MSB;

	//Concat bytes in a 32 bit word
	buffer += this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;

  switch(MSBoption){

    case MSBnull:
      this->data[2] &= ~(1 << 7); //Clear MSB
      buffer += this->data[2] << 16;
      output = (double)buffer;
      output /= pow(2,LSBpow);
    break;

    case MSBsigned:
      MSB = data[2] & 0x80;
  		if(MSB){  //- (2 complement conversion)
  			buffer = ~buffer;
  			//Clearing the first 8 bits
  			for(int i=24; i<32; i++)
  			  buffer &= ~(1 << i);
  			output = (double)buffer + 1.0;
  			output /= -pow(2,LSBpow);
  		}
  		else{     //+
  		  output = (double)buffer;
  			output /= (pow(2,LSBpow)-1.0);
  		}
    break;

    default:
    case MSBunsigned:
      output = (double)buffer;
  		output /= pow(2,LSBpow);
    break;

  }

	return output;
}

/**************************************************************/
/*              PUBLIC METHODS - Instructions                 */
/**************************************************************/
void CS5490::reset(){
	this->instruct(1);
}

void CS5490::standby(){
	this->instruct(2);
}

void CS5490::wakeUp(){
	this->instruct(3);
}

void CS5490::singConv(){
	this->instruct(20);
}

void CS5490::contConv(){
	this->instruct(21);
}

void CS5490::haltConv(){
	this->instruct(24);
}

/**************************************************************/
/*       PUBLIC METHODS - Calibration and Configuration       */
/**************************************************************/

/* SET */
void CS5490::setBaudRate(long value){

	//Calculate the correct binary value
	uint32_t hexBR = ceil(value*0.5242880/MCLK);
	if (hexBR > 65535) hexBR = 65535;
	hexBR += 0x020000;

	this->write(0x80,0x07,hexBR);
	delay(100); //To avoid bugs from ESP32

	//Reset Serial communication from controller
	cSerial->end();
	cSerial->begin(value);
	return;
}

/* GET */
int CS5490::getGainI(){
	//Page 16, Address 33
	this->read(16,33);
	return this->toDouble(22,MSBunsigned);
}

long CS5490::getBaudRate(){
	this->read(0,7);
	uint32_t buffer = this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;
	buffer -= 0x020000;
	return ( (buffer/0.5242880)*MCLK );
}

/**************************************************************/
/*              PUBLIC METHODS - Measurements                 */
/**************************************************************/

double CS5490::getPeakV(){
	//Page 0, Address 36
	this->read(0,36);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPeakI(){
	//Page 0, Address 37
	this->read(0,37);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstI(){
	//Page 16, Address 2
	this->read(16,2);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstV(){
	//Page 16, Address 3
	this->read(16,3);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstP(){
	//Page 16, Address 4
	this->read(16,4);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getRmsI(){
	//Page 16, Address 6
	this->read(16,6);
	return this->toDouble(23, MSBunsigned);
}

double CS5490::getRmsV(){
	//Page 16, Address 7
	this->read(16,7);
	return this->toDouble(23, MSBunsigned);
}

double CS5490::getAvgP(){
	//Page 16, Address 5
	this->read(16,5);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgQ(){
	//Page 16, Address 14
	this->read(16,14);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgS(){
	//Page 16, Address 20
	this->read(16,20);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstQ(){
	//Page 16, Address 15
	this->read(16,15);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPF(){
	//Page 16, Address 21
	this->read(16,21);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalP(){
	//Page 16, Address 29
	this->read(16,29);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalS(){
	//Page 16, Address 30
	this->read(16,30);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalQ(){
	//Page 16, Address 31
	this->read(16,31);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getFreq(){
	//Page 16, Address 49
	this->read(16,49);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTime(){
	//Page 16, Address 49
	this->read(16,61);
	return this->toDouble(0, MSBunsigned);
}

/**************************************************************/
/*              PUBLIC METHODS - Read Register                */
/**************************************************************/

long CS5490::readReg(int page, int address){
	long value = 0;
	this->read(page, address);
	for(int i=0; i<3; i++){
		value += data[2-i];
		value <<= 8;
	}
	return value;
}
<commit_msg>Fix Arduino UNO bug<commit_after>/******************************************

	Author: Tiago Britto Lobão
	tiago.blobao@gmail.com
*/

/*
	Purpose: Control an integrated circuit
	Cirrus Logic - CS5490

	Used to measure electrical quantities

	MIT License

******************************************/

#include "CS5490.h"

/******* Init CS5490 *******/

//For Arduino & ESP8622
#if !(defined ARDUINO_NodeMCU_32S ) && !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(ARDUINO_Node32s)
	CS5490::CS5490(float mclk, int rx, int tx){
		this->MCLK = mclk;
		this->cSerial = new SoftwareSerial(rx,tx);
	}
//For ESP32 AND MEGA
#else
	CS5490::CS5490(float mclk){
		this->MCLK = mclk;
		this->cSerial = &Serial2;
	}
#endif

void CS5490::begin(int baudRate){
	cSerial->begin(baudRate);
	delay(10); //Avoid Bugs on Arduino UNO
}

/**************************************************************/
/*                     PRIVATE METHODS                        */
/**************************************************************/

/******* Write a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::write(int page, int address, long value){

	uint8_t checksum = 0;
	for(int i=0; i<3; i++)
		checksum += 0xFF - checksum;

	//Select page and address
	uint8_t buffer = (pageByte | (uint8_t)page);
	cSerial->write(buffer);
	buffer = (writeByte | (uint8_t)address);
	cSerial->write(buffer);

	//Send information
	for(int i=0; i<3 ; i++){
		data[i] = value & 0x000000FF;
		cSerial->write(this->data[i]);
		value >>= 8;
	}
	//Calculate and send checksum
	buffer = 0xFF - data[0] - data[1] - data[2];
	cSerial->write(buffer);
}

/******* Read a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::read(int page, int address){

	cSerial->flush();

	uint8_t buffer = (pageByte | (uint8_t)page);
	cSerial->write(buffer);
	buffer = (readByte | (uint8_t)address);
	cSerial->write(buffer);

	//Wait for 3 bytes to arrive
	while(cSerial->available() < 3);
	for(int i=0; i<3; i++){
		data[i] = cSerial->read();
	}
}

/******* Give an instruction by the serial communication *******/

void CS5490::instruct(int value){

	cSerial->flush();

	uint8_t buffer = (instructionByte | (uint8_t)value);
	cSerial->write(buffer);
}

/*
  Function: toDouble
  Transforms a 24 bit number to a double number for easy processing data

  Param:
  data[] => Array with size 3. Each uint8_t is an 8 byte number received from CS5490
  LSBpow => Expoent specified from datasheet of the less significant bit
  MSBoption => Information of most significant bit case. It can be only three values:
    MSBnull (1)  The MSB is a Don't Care bit
    MSBsigned (2) the MSB is a negative value, requiring a 2 complement conversion
    MSBunsigned (3) The MSB is a positive value, the default case.

*/
double CS5490::toDouble(int LSBpow, int MSBoption){

	uint32_t buffer = 0;
	double output = 0.0;
	bool MSB;

	//Concat bytes in a 32 bit word
	buffer += this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;

  switch(MSBoption){

    case MSBnull:
      this->data[2] &= ~(1 << 7); //Clear MSB
      buffer += this->data[2] << 16;
      output = (double)buffer;
      output /= pow(2,LSBpow);
    break;

    case MSBsigned:
      MSB = data[2] & 0x80;
  		if(MSB){  //- (2 complement conversion)
  			buffer = ~buffer;
  			//Clearing the first 8 bits
  			for(int i=24; i<32; i++)
  			  buffer &= ~(1 << i);
  			output = (double)buffer + 1.0;
  			output /= -pow(2,LSBpow);
  		}
  		else{     //+
  		  output = (double)buffer;
  			output /= (pow(2,LSBpow)-1.0);
  		}
    break;

    default:
    case MSBunsigned:
      output = (double)buffer;
  		output /= pow(2,LSBpow);
    break;

  }

	return output;
}

/**************************************************************/
/*              PUBLIC METHODS - Instructions                 */
/**************************************************************/
void CS5490::reset(){
	this->instruct(1);
}

void CS5490::standby(){
	this->instruct(2);
}

void CS5490::wakeUp(){
	this->instruct(3);
}

void CS5490::singConv(){
	this->instruct(20);
}

void CS5490::contConv(){
	this->instruct(21);
}

void CS5490::haltConv(){
	this->instruct(24);
}

/**************************************************************/
/*       PUBLIC METHODS - Calibration and Configuration       */
/**************************************************************/

/* SET */
void CS5490::setBaudRate(long value){

	//Calculate the correct binary value
	uint32_t hexBR = ceil(value*0.5242880/MCLK);
	if (hexBR > 65535) hexBR = 65535;
	hexBR += 0x020000;

	this->write(0x80,0x07,hexBR);
	delay(100); //To avoid bugs from ESP32

	//Reset Serial communication from controller
	cSerial->end();
	cSerial->begin(value);
	return;
}

/* GET */
int CS5490::getGainI(){
	//Page 16, Address 33
	this->read(16,33);
	return this->toDouble(22,MSBunsigned);
}

long CS5490::getBaudRate(){
	this->read(0,7);
	uint32_t buffer = this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;
	buffer -= 0x020000;
	return ( (buffer/0.5242880)*MCLK );
}

/**************************************************************/
/*              PUBLIC METHODS - Measurements                 */
/**************************************************************/

double CS5490::getPeakV(){
	//Page 0, Address 36
	this->read(0,36);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPeakI(){
	//Page 0, Address 37
	this->read(0,37);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstI(){
	//Page 16, Address 2
	this->read(16,2);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstV(){
	//Page 16, Address 3
	this->read(16,3);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstP(){
	//Page 16, Address 4
	this->read(16,4);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getRmsI(){
	//Page 16, Address 6
	this->read(16,6);
	return this->toDouble(23, MSBunsigned);
}

double CS5490::getRmsV(){
	//Page 16, Address 7
	this->read(16,7);
	return this->toDouble(23, MSBunsigned);
}

double CS5490::getAvgP(){
	//Page 16, Address 5
	this->read(16,5);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgQ(){
	//Page 16, Address 14
	this->read(16,14);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgS(){
	//Page 16, Address 20
	this->read(16,20);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstQ(){
	//Page 16, Address 15
	this->read(16,15);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPF(){
	//Page 16, Address 21
	this->read(16,21);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalP(){
	//Page 16, Address 29
	this->read(16,29);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalS(){
	//Page 16, Address 30
	this->read(16,30);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalQ(){
	//Page 16, Address 31
	this->read(16,31);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getFreq(){
	//Page 16, Address 49
	this->read(16,49);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTime(){
	//Page 16, Address 49
	this->read(16,61);
	return this->toDouble(0, MSBunsigned);
}

/**************************************************************/
/*              PUBLIC METHODS - Read Register                */
/**************************************************************/

long CS5490::readReg(int page, int address){
	long value = 0;
	this->read(page, address);
	for(int i=0; i<3; i++){
		value += data[2-i];
		value <<= 8;
	}
	return value;
}
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"

#define TEST
//#define REAL

//#define WINDOWS
#define MAC

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

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

void MainWindow::exit()
{
    close();
    qApp->quit();
}

void MainWindow::on_patchButton_clicked()
{
    //Search kext file
    if(searchKernelExtensionFile(&kernelFile))
    {
        //Display Warning Message
        //TODO : Uncomment
        //int answer = QMessageBox::question(this, "Warning", "This will patch the kernel configuration file.\nAre you sure you want to procede ?", QMessageBox::Yes | QMessageBox::No);

        //if (answer == QMessageBox::Yes)
        if(1)
        {
            patchKernelExtensionFile(&kernelFile);
        }
        else
        {
            return;
        }
    }
    else
    {
        return;
    }
}

void MainWindow::on_restoreButton_clicked()
{
    //Display Warning Message
    int answer = QMessageBox::question(this, "Warning", "This will restore the old configuration.\nAre you sure you want to procede ?", QMessageBox::Yes | QMessageBox::No);

    if (answer == QMessageBox::Yes)
    {

    }
    else
    {
        return;
    }
}

bool MainWindow::init()
{
    bool isInitOk = true;

    MainWindow::setWindowTitle (APP_NAME);

    //Search for compatibility
    if(isCompatibleVersion(getMBPModelVersion()))
    {
        isInitOk = true;
    }
    else
    {
        QMessageBox::information(this,"Mac not compatible","Sorry, your Mac is not compatible.\nThe application will close");
        isInitOk = false;
    }

    //Search for SIP Status
    if(isSIPEnabled())
    {
        ui->patchButton->setEnabled(false);
        ui->restoreButton->setEnabled(false);

        QMessageBox msgBox;
        msgBox.setInformativeText("The System Integrity Protection is enabled\nPlease follow the instructions to disable it");
        msgBox.setWindowTitle("SIP Enabled");

        QAbstractButton* pButtonYes = msgBox.addButton(tr("Take me to tutorial"), QMessageBox::YesRole);
        msgBox.addButton(tr("Nope"), QMessageBox::NoRole);
        msgBox.setIcon(QMessageBox::Information);
        msgBox.exec();

        if (msgBox.clickedButton()== pButtonYes)
        {
            QString link = "https://www.youtube.com/watch?v=Wmhal4shmVo";
            QDesktopServices::openUrl(QUrl(link));
        }
    }

    return isInitOk;
}

QString MainWindow::getMBPModelVersion()
{
    QString MBPModelVersion;
    QProcess process;

    //Execute commande line
    process.start("sysctl -n hw.model");

    //Wait forever until finished
    process.waitForFinished(-1);

    //Get command line output
    MBPModelVersion = process.readAllStandardOutput();

    //Remove carriage return ("\n") from string
    MBPModelVersion = MBPModelVersion.simplified();

    return MBPModelVersion;
}

bool MainWindow::isSIPEnabled(void)
{
    QString SIPStatus;
    QProcess process;

    //Execute commande line
    process.start("csrutil status");

    //Wait forever until finished
    process.waitForFinished(-1);

    //Get command line output
    SIPStatus = process.readAllStandardOutput();

#ifndef WINDOWS
    if(SIPStatus.contains("disable"))
    {
        return false;
    }
    else
    {
        return true;
    }
    #else
      return false;
    #endif
}

//Parse system directory searching for AppleGraphicsPowerManagement.kext file
bool MainWindow::searchKernelExtensionFile(QFile* kernelExtensionFile)
{
    bool isFileFound;

#ifdef TEST
#ifdef MAC
    QDir kextPath("/Users/Julian/Documents/Dev/Projects/MBPMid2010_GPUFix/");
#endif
#ifdef WINDOWS
    QDir kextPath("C:/Users/jpoidevin/Desktop/Documents Pro/03 - Dev Temp/MBPMid2010_GPUFix/MBPMid2010_GPUFix/");
#endif
#endif
#ifdef REAL
    QDir kextPath("/System/Library/Extensions/AppleGraphicsPowerManagement.kext/");
#endif

    QStringList listOfFiles;

    //Print Current app directory
    qDebug() << "Current Dir :" <<kextPath.absolutePath();

    //Recursively search for "Info.plist" file in appPath
    QDirIterator it(kextPath.absolutePath(),
                    QStringList() << "Info.plist",
                    QDir::NoSymLinks | QDir::Files,
                    QDirIterator::Subdirectories);

    //Check if the file was found
    if(it.hasNext())
    {
        while(it.hasNext())
        {
            it.next();
            if (it.filePath().contains("AppleGraphicsPowerManagement"))
            {
                listOfFiles.push_back(it.filePath());
            }
        }
    }

    //Print files found
    qDebug() << "Files found :"<< listOfFiles;

    if(listOfFiles.length() <= 1 && listOfFiles.length() > 0)
    {
        //qDebug() << "Moins de 1";
        kernelExtensionFile->setFileName(listOfFiles.at(0));
        isFileFound = true;
    }
    else
    {
        //qDebug () << "No file was found...";
        isFileFound = false;
    }

    //Start search manually and only allow loading of the perfect named file (or kext)
    if(!isFileFound)
    {
        QMessageBox::information(this,"File not found","Any corresponding file was found, please search for the file");

        //TODO : FileDialog won't let user browse into .kext files Contents
        QString dir = QFileDialog::getOpenFileName(this, tr("Open Info.plist file"),
                                                   "/System/Library/Extensions/AppleGraphicsPowerManagement.kext/",
                                                   "Property List Files (Info.plist)");
        if(!(dir.isNull()))
        {
            //kernelExtensionFile->setFileName(dir);
            isFileFound = true;
        }
        else
        {
            isFileFound = false;
        }
    }

    return isFileFound;
}

bool MainWindow::isCompatibleVersion(QString modelVersion)
{
    //Compare version with compatible versions of MBPs
    bool isCompatibleVersion;

#ifdef MAC

    //TODO : Search in a list if several models compatible
    if(modelVersion == "MacBookPro6,2")
    {
        isCompatibleVersion = true;
    }
    else
    {
        isCompatibleVersion = false;
    }

#endif
#ifdef WINDOWS
    isCompatibleVersion = true;
#endif

    return isCompatibleVersion;
}

void MainWindow::backupOldKernelExtension()
{
    //Save File to current location adding .bak extension
    //qDebug() << "File Name" << kernelFile.fileName();

    //Save original file in kernelExtension file folder
    QFile::copy(kernelFile.fileName(), kernelFile.fileName() + ".bak");
}

void MainWindow::patchKernelExtensionFile(QFile *kernelFile)
{
    //Modify Kernel Extension File to add fix explained here :
    //https://forums.macrumors.com/threads/gpu-kernel-panic-in-mid-2010-whats-the-best-fix.1890097/
    //Use QSettings ? : http://doc.qt.io/qt-5/qsettings.html
    //https://openclassrooms.com/courses/enregistrer-vos-options-avec-qsettings
    //http://stackoverflow.com/questions/20240511/qsettings-mac-and-plist-files
    //https://forum.qt.io/topic/37247/qsettings-with-systemscope-not-saving-plist-file-in-os-x-mavericks/6

    //TODO
    //backupOldKernelExtension();

#ifdef MAC
#define PATCHED_FILE_PATH "/tmp/PatchedInfo.plist"
#endif
#ifdef WINDOWS
#define PATCHED_FILE_PATH "C:/temp/PatchedInfo.plist"
#endif

    //Remove file if already exists
    if (QFile::exists(PATCHED_FILE_PATH))
    {
        QFile::remove(PATCHED_FILE_PATH);
    }

    //Copy file in tmp dir for patch
    QFile::copy(kernelFile->fileName(), PATCHED_FILE_PATH);

    QFile tmpFile(PATCHED_FILE_PATH);
    if(!tmpFile.open(QIODevice::ReadWrite | QIODevice::Text))
    {
        qDebug() << "Could not open tmp File";
        return;
    }

    //The QDomDocument class represents an XML document.
    QDomDocument xmlBOM;

    // Set data into the QDomDocument before processing
    xmlBOM.setContent(&tmpFile);

    /* Definition of struct and enum to automaticaly parse file*/
    typedef enum
    {
        FindChild,
        FindSibling,
        NextSibling,
        FirstChild,
        FillArray
    }EActions;

    typedef struct{
        QString nodeName;
        QVector<int> ArrayValues;
        EActions ActionToPerform;
    }nodeTree;

    QVector<nodeTree> confTree={
        {"MacBookPro6,2"        , {}            , FindChild    },
        {"dict"                 , {}            , NextSibling  },
        {"Vendor10deDevice0a29" , {}            , FindChild    },
        {"BoostPState"          , {}            , FindSibling  },
        {""                     , {2,2,2,2}     , FillArray    },
        {"BoostTime"            , {}            , FindSibling  },
        {""                     , {2,2,2,2}     , FillArray    },
        {"Heuristic"            , {}            , FindSibling  },
        {"Threshold_High"       , {}            , FindSibling  },
        {""                     , {0,0,100,200} , FillArray    },
        {"Threshold_High_v"     , {}            , FindSibling  },
        {""                     , {0,0,98,100}  , FillArray    },
        {"Threshold_Low"        , {}            , FindSibling  },
        {""                     , {0,0,0,200}   , FillArray    },
        {"Threshold_Low_v"      , {}            , FindSibling  },
        {""                     , {0,0,4,200}   , FillArray    }
    };

    QDomElement currentNode = xmlBOM.firstChildElement("plist");
    QDomElement nextNode;

    for (int i = 0; i < confTree.size(); ++i)
    {
        //qDebug() << confTree.at(i).nodeName << confTree.at(i).ActionToPerform;
        switch (confTree.at(i).ActionToPerform){
        case FindChild:
            nextNode = findElementChild(currentNode,confTree.at(i).nodeName);
            qDebug() << "FindChild - " << nextNode.tagName() << "|" << nextNode.text();
            break;

        case FindSibling:
            nextNode = findElementSibling(currentNode,confTree.at(i).nodeName);
            qDebug() << "FindSibling - " << nextNode.tagName() << "|" << nextNode.text();
            break;

        case NextSibling:
            nextNode = currentNode.nextSiblingElement(confTree.at(i).nodeName);
            qDebug() << "NextSibling - " << nextNode.tagName();
            break;

        case FirstChild:
            nextNode = currentNode.firstChildElement(confTree.at(i).nodeName);
            qDebug() << "FirstChild - " << nextNode.tagName();
            break;

        case FillArray:

            currentNode = currentNode.nextSiblingElement("array").firstChildElement("integer");

            currentNode.firstChild().setNodeValue(QString::number(confTree.at(i).ArrayValues[0]));
            currentNode.nextSibling().firstChild().setNodeValue(QString::number(confTree.at(i).ArrayValues[1]));
            currentNode.nextSibling().nextSibling().firstChild().setNodeValue(QString::number(confTree.at(i).ArrayValues[2]));
            currentNode.nextSibling().nextSibling().nextSibling().firstChild().setNodeValue(QString::number(confTree.at(i).ArrayValues[3]));

            nextNode = currentNode.parentNode().toElement();

            break;

        default:
            break;
        }

        currentNode = nextNode;
    }

    // Write changes to same file
    tmpFile.resize(0);
    QTextStream stream;
    stream.setDevice(&tmpFile);
    xmlBOM.save(stream, 4);

    tmpFile.close();
}

int MainWindow::loadKernelExtension(QFile *kernelFile)
{
    //Use Kext Utility or command lines utils to load the file in Kernel
    //kextload

    //Disable srcutils: https://derflounder.wordpress.com/2015/10/05/configuring-system-integrity-protection-without-booting-to-recovery-hd/

    //See here : http://osxdaily.com/2015/06/24/load-unload-kernel-extensions-mac-os-x/
    int Status = 0;

    return Status;
}

int MainWindow::restoreOldKernelExtension(QFile *kernelFile)
{
    //Restore.bak extension
    int Status = 0;

    //QFile::copy(kernelFile->fileName()  + ".bak", kernelFile->fileName());

    return Status;
}

QDomElement MainWindow::findElementChild(QDomElement parent, const QString &textToFind)
{
    for(QDomElement elem = parent.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement())
    {
        if(elem.text()==textToFind) return elem;

        QDomElement e = findElementChild(elem, textToFind);

        if(!e.isNull()) return e;
    }

    return QDomElement();
}

QDomElement MainWindow::findElementSibling(QDomElement parent, const QString &textToFind)
{
    for(QDomElement elem = parent.nextSiblingElement(); !elem.isNull(); elem = elem.nextSiblingElement())
    {
        if(elem.text()==textToFind) return elem;

        QDomElement e = findElementChild(elem, textToFind);

        if(!e.isNull()) return e;
    }

    return QDomElement();
}
<commit_msg>Started working on kernel extension loading<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"

//#define TEST
#define REAL

//#define WINDOWS
#define MAC

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

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

void MainWindow::exit()
{
    close();
    qApp->quit();
}

void MainWindow::on_patchButton_clicked()
{
    //Search kext file
    if(searchKernelExtensionFile(&kernelFile))
    {
        //Display Warning Message
        //TODO : Uncomment
        //int answer = QMessageBox::question(this, "Warning", "This will patch the kernel configuration file.\nAre you sure you want to procede ?", QMessageBox::Yes | QMessageBox::No);

        //if (answer == QMessageBox::Yes)
        if(1)
        {
            patchKernelExtensionFile(&kernelFile);
            loadKernelExtension(&kernelFile);
        }
        else
        {
            return;
        }
    }
    else
    {
        return;
    }
}

void MainWindow::on_restoreButton_clicked()
{
    //Display Warning Message
    int answer = QMessageBox::question(this, "Warning", "This will restore the old configuration.\nAre you sure you want to procede ?", QMessageBox::Yes | QMessageBox::No);

    if (answer == QMessageBox::Yes)
    {

    }
    else
    {
        return;
    }
}

bool MainWindow::init()
{
    bool isInitOk = true;

    MainWindow::setWindowTitle (APP_NAME);

    //Search for compatibility
    if(isCompatibleVersion(getMBPModelVersion()))
    {
        isInitOk = true;
    }
    else
    {
        QMessageBox::information(this,"Mac not compatible","Sorry, your Mac is not compatible.\nThe application will close");
        isInitOk = false;
    }

    //Search for SIP Status
    if(isSIPEnabled())
    {
        ui->patchButton->setEnabled(false);
        ui->restoreButton->setEnabled(false);

        QMessageBox msgBox;
        msgBox.setInformativeText("The System Integrity Protection is enabled\nPlease follow the instructions to disable it");
        msgBox.setWindowTitle("SIP Enabled");

        QAbstractButton* pButtonYes = msgBox.addButton(tr("Take me to tutorial"), QMessageBox::YesRole);
        msgBox.addButton(tr("Nope"), QMessageBox::NoRole);
        msgBox.setIcon(QMessageBox::Information);
        msgBox.exec();

        if (msgBox.clickedButton()== pButtonYes)
        {
            QString link = "https://www.youtube.com/watch?v=Wmhal4shmVo";
            QDesktopServices::openUrl(QUrl(link));
        }
    }

    return isInitOk;
}

QString MainWindow::getMBPModelVersion()
{
    QString MBPModelVersion;
    QProcess process;

    //Execute commande line
    process.start("sysctl -n hw.model");

    //Wait forever until finished
    process.waitForFinished(-1);

    //Get command line output
    MBPModelVersion = process.readAllStandardOutput();

    //Remove carriage return ("\n") from string
    MBPModelVersion = MBPModelVersion.simplified();

    return MBPModelVersion;
}

bool MainWindow::isSIPEnabled(void)
{
    QString SIPStatus;
    QProcess process;

    //Execute commande line
    process.start("csrutil status");

    //Wait forever until finished
    process.waitForFinished(-1);

    //Get command line output
    SIPStatus = process.readAllStandardOutput();

#ifndef WINDOWS
    if(SIPStatus.contains("disable"))
    {
        return false;
    }
    else
    {
        return true;
    }
#else
    return false;
#endif
}

//Parse system directory searching for AppleGraphicsPowerManagement.kext file
bool MainWindow::searchKernelExtensionFile(QFile* kernelExtensionFile)
{
    bool isFileFound;

#ifdef TEST
#ifdef MAC
    QDir kextPath("/Users/Julian/Documents/Dev/Projects/MBPMid2010_GPUFix/");
#endif
#ifdef WINDOWS
    QDir kextPath("C:/Users/jpoidevin/Desktop/Documents Pro/03 - Dev Temp/MBPMid2010_GPUFix/MBPMid2010_GPUFix/");
#endif
#endif
#ifdef REAL
    QDir kextPath("/System/Library/Extensions/AppleGraphicsPowerManagement.kext/");
#endif

    QStringList listOfFiles;

    //Print Current app directory
    qDebug() << "Current Dir :" <<kextPath.absolutePath();

    //Recursively search for "Info.plist" file in appPath
    QDirIterator it(kextPath.absolutePath(),
                    QStringList() << "Info.plist",
                    QDir::NoSymLinks | QDir::Files,
                    QDirIterator::Subdirectories);

    //Check if the file was found
    if(it.hasNext())
    {
        while(it.hasNext())
        {
            it.next();
            if (it.filePath().contains("AppleGraphicsPowerManagement"))
            {
                listOfFiles.push_back(it.filePath());
            }
        }
    }

    //Print files found
    qDebug() << "Files found :"<< listOfFiles;

    if(listOfFiles.length() <= 1 && listOfFiles.length() > 0)
    {
        //qDebug() << "Moins de 1";
        kernelExtensionFile->setFileName(listOfFiles.at(0));
        isFileFound = true;
    }
    else
    {
        //qDebug () << "No file was found...";
        isFileFound = false;
    }

    //Start search manually and only allow loading of the perfect named file (or kext)
    if(!isFileFound)
    {
        QMessageBox::information(this,"File not found","Any corresponding file was found, please search for the file");

        //TODO : FileDialog won't let user browse into .kext files Contents
        QString dir = QFileDialog::getOpenFileName(this, tr("Open Info.plist file"),
                                                   "/System/Library/Extensions/AppleGraphicsPowerManagement.kext/",
                                                   "Property List Files (Info.plist)");
        if(!(dir.isNull()))
        {
            //kernelExtensionFile->setFileName(dir);
            isFileFound = true;
        }
        else
        {
            isFileFound = false;
        }
    }

    return isFileFound;
}

bool MainWindow::isCompatibleVersion(QString modelVersion)
{
    //Compare version with compatible versions of MBPs
    bool isCompatibleVersion;

#ifdef MAC

    //TODO : Search in a list if several models compatible
    if(modelVersion == "MacBookPro6,2")
    {
        isCompatibleVersion = true;
    }
    else
    {
        isCompatibleVersion = false;
    }

#endif
#ifdef WINDOWS
    isCompatibleVersion = true;
#endif

    return isCompatibleVersion;
}

void MainWindow::backupOldKernelExtension()
{
    //Save File to current location adding .bak extension
    //qDebug() << "File Name" << kernelFile.fileName();

    //Save original file in kernelExtension file folder
    QFile::copy(kernelFile.fileName(), kernelFile.fileName() + ".bak");
}

void MainWindow::patchKernelExtensionFile(QFile *kernelFile)
{
    //Modify Kernel Extension File to add fix explained here :
    //https://forums.macrumors.com/threads/gpu-kernel-panic-in-mid-2010-whats-the-best-fix.1890097/
    //Use QSettings ? : http://doc.qt.io/qt-5/qsettings.html
    //https://openclassrooms.com/courses/enregistrer-vos-options-avec-qsettings
    //http://stackoverflow.com/questions/20240511/qsettings-mac-and-plist-files
    //https://forum.qt.io/topic/37247/qsettings-with-systemscope-not-saving-plist-file-in-os-x-mavericks/6

    //TODO
    //backupOldKernelExtension();

#ifdef MAC
#define PATCHED_FILE_PATH "/tmp/PatchedInfo.plist"
#endif
#ifdef WINDOWS
#define PATCHED_FILE_PATH "C:/temp/PatchedInfo.plist"
#endif

    //Remove file if already exists
    if (QFile::exists(PATCHED_FILE_PATH))
    {
        QFile::remove(PATCHED_FILE_PATH);
    }

    //Copy file in tmp dir for patch
    QFile::copy(kernelFile->fileName(), PATCHED_FILE_PATH);

    QFile tmpFile(PATCHED_FILE_PATH);
    if(!tmpFile.open(QIODevice::ReadWrite | QIODevice::Text))
    {
        qDebug() << "Could not open tmp File";
        return;
    }

    //The QDomDocument class represents an XML document.
    QDomDocument xmlBOM;

    // Set data into the QDomDocument before processing
    xmlBOM.setContent(&tmpFile);

    /* Definition of struct and enum to automaticaly parse file*/
    typedef enum
    {
        FindChild,
        FindSibling,
        NextSibling,
        FirstChild,
        FillArray
    }EActions;

    typedef struct{
        QString nodeName;
        QVector<int> ArrayValues;
        EActions ActionToPerform;
    }nodeTree;

    QVector<nodeTree> confTree={
        {"MacBookPro6,2"        , {}            , FindChild    },
        {"dict"                 , {}            , NextSibling  },
        {"Vendor10deDevice0a29" , {}            , FindChild    },
        {"BoostPState"          , {}            , FindSibling  },
        {""                     , {2,2,2,2}     , FillArray    },
        {"BoostTime"            , {}            , FindSibling  },
        {""                     , {2,2,2,2}     , FillArray    },
        {"Heuristic"            , {}            , FindSibling  },
        {"Threshold_High"       , {}            , FindSibling  },
        {""                     , {0,0,100,200} , FillArray    },
        {"Threshold_High_v"     , {}            , FindSibling  },
        {""                     , {0,0,98,100}  , FillArray    },
        {"Threshold_Low"        , {}            , FindSibling  },
        {""                     , {0,0,0,200}   , FillArray    },
        {"Threshold_Low_v"      , {}            , FindSibling  },
        {""                     , {0,0,4,200}   , FillArray    }
    };

    QDomElement currentNode = xmlBOM.firstChildElement("plist");
    QDomElement nextNode;

    for (int i = 0; i < confTree.size(); ++i)
    {
        //qDebug() << confTree.at(i).nodeName << confTree.at(i).ActionToPerform;
        switch (confTree.at(i).ActionToPerform){
        case FindChild:
            nextNode = findElementChild(currentNode,confTree.at(i).nodeName);
            qDebug() << "FindChild - " << nextNode.tagName() << "|" << nextNode.text();
            break;

        case FindSibling:
            nextNode = findElementSibling(currentNode,confTree.at(i).nodeName);
            qDebug() << "FindSibling - " << nextNode.tagName() << "|" << nextNode.text();
            break;

        case NextSibling:
            nextNode = currentNode.nextSiblingElement(confTree.at(i).nodeName);
            qDebug() << "NextSibling - " << nextNode.tagName();
            break;

        case FirstChild:
            nextNode = currentNode.firstChildElement(confTree.at(i).nodeName);
            qDebug() << "FirstChild - " << nextNode.tagName();
            break;

        case FillArray:

            currentNode = currentNode.nextSiblingElement("array").firstChildElement("integer");

            currentNode.firstChild().setNodeValue(QString::number(confTree.at(i).ArrayValues[0]));
            currentNode.nextSibling().firstChild().setNodeValue(QString::number(confTree.at(i).ArrayValues[1]));
            currentNode.nextSibling().nextSibling().firstChild().setNodeValue(QString::number(confTree.at(i).ArrayValues[2]));
            currentNode.nextSibling().nextSibling().nextSibling().firstChild().setNodeValue(QString::number(confTree.at(i).ArrayValues[3]));

            nextNode = currentNode.parentNode().toElement();

            break;

        default:
            break;
        }

        currentNode = nextNode;
    }

    // Write changes to same file
    tmpFile.resize(0);
    QTextStream stream;
    stream.setDevice(&tmpFile);
    xmlBOM.save(stream, 4);

    tmpFile.close();
}

int MainWindow::loadKernelExtension(QFile *kernelFile)
{
    //Use Kext Utility or command lines utils to load the file in Kernel
    //kextload

    //Disable srcutils: https://derflounder.wordpress.com/2015/10/05/configuring-system-integrity-protection-without-booting-to-recovery-hd/

    /* Copy real kext into tmp file */
    QProcess process;
    QString command = "cp";
    QStringList arguments;
    QDir kextDir(kernelFile->fileName());
    kextDir.cdUp();

    arguments << "-rf" << kextDir.absolutePath() << "/tmp/AppleGraphicsPowerManagement.kext";
    //Execute commande line
    process.start(command,arguments);
    //Wait forever until finished
    process.waitForFinished(-1);

    /*** Copy patched file into kext ***/
    command = "cp";
    arguments.clear();
    arguments << "-f" << PATCHED_FILE_PATH << "/tmp/AppleGraphicsPowerManagement.kext/Info.plist";
    //Execute commande line
    process.start(command,arguments);
    //Wait forever until finished
    process.waitForFinished(-1);

    /*** Change permission of modified kext File ***/
    //TODO find a way to execute process as root
    command = "chown";
    arguments.clear();
    arguments << "-R" << "-v" << "root:wheel" << "/tmp/AppleGraphicsPowerManagement.kext/";
    //Execute commande line
    process.start(command,arguments);
    //Wait forever until finished
    process.waitForFinished(-1);
    qDebug() << process.readAllStandardError();

    /*** Unload previous kext file ***/
    //TODO find a way to execute process as root
    command = "kextunload";
    arguments.clear();
    arguments << "-v" << "/System/Library/Extensions/AppleGraphicsPowerManagement.kext";
    //Execute commande line
    process.start(command,arguments);
    //Wait forever until finished
    process.waitForFinished(-1);
    qDebug() << process.readAllStandardError();

    /*** Finally load kext file ***/
    //TODO find a way to execute process as root
    command = "kextload";
    arguments.clear();
    arguments << "-v" << "/tmp/AppleGraphicsPowerManagement.kext";
    //Execute commande line
    process.start(command,arguments);
    //Wait forever until finished
    process.waitForFinished(-1);
    qDebug() << process.readAllStandardError();

    //See here : http://osxdaily.com/2015/06/24/load-unload-kernel-extensions-mac-os-x/
    int Status = 0;

    return Status;
}

int MainWindow::restoreOldKernelExtension(QFile *kernelFile)
{
    //Restore.bak extension
    int Status = 0;

    //QFile::copy(kernelFile->fileName()  + ".bak", kernelFile->fileName());

    return Status;
}

QDomElement MainWindow::findElementChild(QDomElement parent, const QString &textToFind)
{
    for(QDomElement elem = parent.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement())
    {
        if(elem.text()==textToFind) return elem;

        QDomElement e = findElementChild(elem, textToFind);

        if(!e.isNull()) return e;
    }

    return QDomElement();
}

QDomElement MainWindow::findElementSibling(QDomElement parent, const QString &textToFind)
{
    for(QDomElement elem = parent.nextSiblingElement(); !elem.isNull(); elem = elem.nextSiblingElement())
    {
        if(elem.text()==textToFind) return elem;

        QDomElement e = findElementChild(elem, textToFind);

        if(!e.isNull()) return e;
    }

    return QDomElement();
}
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "graphSettingsPopup.h"
#include <QDateTime>

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

#ifdef PMD_HAMP
    ui->pbtn_StartStop->setChecked(true);
#endif
	printf("This is MainWindow\n");
	qDebug("This is MainWindow via QDebug()\n");

    m_alarmBtnNormalStyleSheet=ui->pbtn_ECG_Alarm->styleSheet();
    m_alarmBtnRedStyleSheet= "*{border: 0px;background-image: url(:/images/icnBellRed.png);} *:checked{background-image:url();}";

    m_graphWidget1 = new Widget(ui->graphECGWidget,this);
#ifndef ONLY_SHOW_ECG_GRAPH
    m_graphWidget2 = new Widget(ui->graphABPWidget,this);
    m_graphWidget3 = new Widget(ui->graphPLETHWidget,this);
#endif

    m_graphWidget1->initialized(GraphECG,3);
#ifndef ONLY_SHOW_ECG_GRAPH
    m_graphWidget2->initialized(GraphABP,1);
    m_graphWidget3->initialized(GraphPLETH,2);
#endif

    m_selectedGraphWidget = NULL;

    // Set time
    updateTimeString();

    // Setup timer to update UI
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateTimeString()));
    timer->start(1000);

    m_timerAlarm =new QTimer (this);
    connect(m_timerAlarm, SIGNAL(timeout()), this, SLOT(animateAlarm()));
    m_timerAlarm->setInterval(500);
    m_timerAlarm->stop();

    m_timerDataValues = new QTimer(this);
#ifndef DISABLE_RIGHT_PANEL_NUMERIC_VALUES
    connect(m_timerDataValues, SIGNAL(timeout()), this, SLOT(updatePulse()));
    connect(m_timerDataValues, SIGNAL(timeout()), this, SLOT(updateABP()));
    connect(m_timerDataValues, SIGNAL(timeout()), this, SLOT(updatePLETH()));
    m_timerDataValues->setInterval(3000);
    #ifndef PMD_HAMP
        m_timerDataValues->start();
    #endif
#endif

    m_HB_Simulate_Data << 70 << 68 << 71 << 69<< 67<< 68;
    m_ABP_Higher_Simulate_Data << 100 << 110 << 107 << 124<< 137<< 119;
    m_ABP_Lower_Simulate_Data << 73 << 88 << 77 << 82 << 91 << 79;
    m_PLETH_Simulate_Data << 93 << 96 << 90 << 97 << 95 << 99;

    m_cstatus=true;

#ifdef PMD_MEHV
    m_nserver =new Server(this);
    m_cstatus=false;
#endif

#ifdef PMD_NUCLEUS
    m_dataSupplier=new DataSupplier(this);
#endif

#ifdef PMD_HAMP
	printf("Creating HAMPDataSupplier\n");
    m_hampdataSupplier =new HAMPDataSupplier(this);
    m_cstatus=true;

    // Setup timer to update UI
    QTimer *hamptimer = new QTimer(this);
    connect(hamptimer, SIGNAL(timeout()), this, SLOT(takeScreenSnapshort()));
    hamptimer->start(3000);

#endif

    m_isPauseAll=false;
    m_isStart=true;
    m_isAlarmSilent=false;
    m_btnSilentPressed=false;

    m_graphSettingsPop =new GraphSettingsPopup(this);
    m_graphSettingsPop->hide();

    // Setup graph scrolling mode
#ifdef ENABLE_GRAPH_SCROLLING
    on_pbtn_Scrolling_clicked(true);
    ui->pbtn_Scrolling->setChecked(true);
#else
    on_pbtn_Scrolling_clicked(false);
    ui->pbtn_Scrolling->setChecked(false);
#endif
}

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

void MainWindow::updatePulse()
{
    QString value="0";

    if(m_isPauseAll==true || m_isStart==false)
        return;

    if(m_cstatus)
    {
        value.setNum(this->getPulseValue());
    }

    ui->labelPulse->setText(value);
    ui->labelPulse->repaint();
}

void MainWindow::updateABP()
{

    if(m_isPauseAll==true || m_isStart==false)
        return;

    if(m_cstatus)
    {
      ui->labelABP->setText(this->getABPValue());
    }
    else
        ui->labelABP->setText("0/0");

    ui->labelABP->repaint();
}

void MainWindow::updatePLETH()
{
    if(m_isPauseAll==true || m_isStart==false)
        return;

    if(m_cstatus)
    {
      ui->labelSPO2->setText(this->getPLETHValue());
    }
    else
        ui->labelSPO2->setText("0");

    ui->labelABP->repaint();
}

void MainWindow::updateTimeString()
{
    //get current date and time
    QDateTime dateTime = QDateTime::currentDateTime();
    QString dateTimeString = dateTime.toString("hh:mm:ss AP");
    ui->labelTime->setText(dateTimeString);
    ui->labelTime->repaint();
}

int MainWindow::getPulseValue()
{
    static int index=-1;

    if(index==this->m_HB_Simulate_Data.count()-1)
        index=0;
    else
        index++;

    return m_HB_Simulate_Data[index];
}

QString MainWindow::getABPValue()
{
    static int index=-1;

    if(index==this->m_ABP_Higher_Simulate_Data.count()-1)
        index=0;
    else
        index++;

    QString str=QString("%1/%2").arg(m_ABP_Higher_Simulate_Data[index]).arg(m_ABP_Lower_Simulate_Data[index]);
    return str;
}

QString MainWindow::getPLETHValue()
{
    static int index=-1;

    if(index==this->m_PLETH_Simulate_Data.count()-1)
        index=0;
    else
        index++;

    QString str=QString("%1").arg(m_PLETH_Simulate_Data[index]);
    return str;
}

void MainWindow::on_pbtn_Silent_clicked()
{
   m_btnSilentPressed=!m_btnSilentPressed;
    if(m_btnSilentPressed)
    {
        ui->pbtn_ECG_Alarm->setChecked(true);
        ui->pbtn_ABP_Alarm->setChecked(true);
        ui->pbtn_spo2_Alarm->setChecked(true);
        m_isAlarmSilent=true;
       // ui->pbtn_Silent->setStyleSheet("*{border: 0px;background-image: url(:/images/btn_pressed.png);}");
    }
    else
    {
        ui->pbtn_ECG_Alarm->setChecked(false);
        ui->pbtn_ABP_Alarm->setChecked(false);
        ui->pbtn_spo2_Alarm->setChecked(false);
        m_isAlarmSilent=false;
        //ui->pbtn_Silent->setStyleSheet("");
    }
}

void MainWindow::on_pbtn_StartStop_clicked(bool checked)
{

#ifdef PMD_HAMP

    if(checked)
    {
        m_isStart=false;
        m_timerDataValues->stop();
        ui->pbtn_StartStop->setText("Start");
        m_hampdataSupplier->startStopNucleus(m_isStart);
        m_cstatus=false;
    }
    else
    {
        m_isStart=true;
        ui->pbtn_StartStop->setText("Stop");
        m_timerDataValues->start();
        m_hampdataSupplier->startStopNucleus(m_isStart);
        m_cstatus=true;

    }
   fflush(stdout);
#else

    if(checked)
    {
        m_isStart=false;
        this->m_graphWidget1->clearWidget();
#ifndef ONLY_SHOW_ECG_GRAPH
        this->m_graphWidget2->clearWidget();
        this->m_graphWidget3->clearWidget();
#endif
        ui->pbtn_StartStop->setText("Start");
    }
    else
    {
        m_isStart=true;
        ui->pbtn_StartStop->setText("Stop");
    }

#endif

}

void MainWindow::on_pbtn_PauseAll_clicked(bool checked)
{
    if(!checked)
    {
        m_isPauseAll=false;
        ui->pbtn_PauseAll->setText("Pause All");
    }
    else
    {
        m_isPauseAll=true;
        this->m_graphWidget1->clearWidget();
#ifndef ONLY_SHOW_ECG_GRAPH
        this->m_graphWidget2->clearWidget();
        this->m_graphWidget3->clearWidget();
#endif
        ui->pbtn_PauseAll->setText("Start All");
    }
}

void MainWindow::on_pbtn_Scrolling_clicked(bool checked)
{
 (void)checked;
#ifndef PMD_HAMP
    m_graphWidget1->setScrollingMode(checked);
#ifndef ONLY_SHOW_ECG_GRAPH
    m_graphWidget2->setScrollingMode(checked);
    m_graphWidget3->setScrollingMode(checked);
#endif
#endif
}

void MainWindow::updateTimer()
{
#ifdef PMD_MEHV
    pm_data_struct pm={0,0,0,0};
    dataReceived (&pm);
#endif
}

void MainWindow::animateAlarm()
{
    ui->pbtn_ABP_Alarm->toggle();
    ui->pbtn_ECG_Alarm->toggle();
    ui->pbtn_spo2_Alarm->toggle();
}

void MainWindow::on_pbtn_ECG_Alarm_clicked(bool checked)
{
    (void)checked;
}

void MainWindow::on_pbtn_spo2_Alarm_clicked(bool checked)
{
    (void)checked;
}

void MainWindow::on_pbtn_ABP_Alarm_clicked(bool checked)
{
    (void)checked;
}

void MainWindow::dataReceived(pm_data_struct *current)
{
	if (m_isPauseAll==false)
    {
		switch(m_graphWidget1->getGraphType())
        {

        case GraphECG:
            m_graphWidget1->animate(current->ecgValue);
        break;

        case GraphABP:
            m_graphWidget1->animate(current->abpValue);
        break;

        case GraphPLETH:
            m_graphWidget1->animate(current->plethValue);
        break;

        case GraphCO2:
            //TODO
        break;
        }

#ifndef ONLY_SHOW_ECG_GRAPH
        switch(m_graphWidget2->getGraphType())
        {
        case GraphECG:
            m_graphWidget2->animate(current->ecgValue);
        break;

        case GraphABP:
            m_graphWidget2->animate(current->abpValue);
        break;

        case GraphPLETH:
            m_graphWidget2->animate(current->plethValue);
        break;

        case GraphCO2:
            //TODO
        break;
        }

        switch(m_graphWidget3->getGraphType())
        {
        case GraphECG:
            m_graphWidget3->animate(current->ecgValue);
        break;

        case GraphABP:
            m_graphWidget3->animate(current->abpValue);
        break;

        case GraphPLETH:
            m_graphWidget3->animate(current->plethValue);
        break;

        case GraphCO2:
            //TODO
        break;
        }
#endif
    }
}

#ifndef PMD_NUCLEUS
void MainWindow::connectionStatus(bool status)
{
    QDateTime dateTime = QDateTime::currentDateTime();
    QString dateTimeString = dateTime.toString("hh:mmap");
    m_cstatus=status;

    if(status)
    {
        ui->labelBulletText->setText("External system connected at "+dateTimeString);
        ui->labelBulletText->repaint();
        ui->pbtn_ECG_Alarm->setStyleSheet(m_alarmBtnNormalStyleSheet);
        ui->pbtn_ABP_Alarm->setStyleSheet(m_alarmBtnNormalStyleSheet);
        ui->pbtn_spo2_Alarm->setStyleSheet(m_alarmBtnNormalStyleSheet);
        ui->pbtn_ABP_Alarm->setDisabled(false);
        ui->pbtn_ECG_Alarm->setDisabled(false);
        ui->pbtn_spo2_Alarm->setDisabled(false);
        m_timerAlarm->stop();

    }
    else
    {
        ui->labelBulletText->setText("External system disconnected at "+dateTimeString);
        ui->labelBulletText->repaint();
        ui->pbtn_ECG_Alarm->setStyleSheet(m_alarmBtnRedStyleSheet);
        ui->pbtn_ABP_Alarm->setStyleSheet(m_alarmBtnRedStyleSheet);
        ui->pbtn_spo2_Alarm->setStyleSheet(m_alarmBtnRedStyleSheet);
        ui->pbtn_ABP_Alarm->setDisabled(true);
        ui->pbtn_ECG_Alarm->setDisabled(true);
        ui->pbtn_spo2_Alarm->setDisabled(true);

        m_timerAlarm->start();
        updatePLETH();
        updateABP();
        updatePulse();
    }
}
#endif

void MainWindow::launchGraphMenuPopup(Widget *widget)
{
   if(m_graphSettingsPop->m_isVisible==false)
   {
       m_selectedGraphWidget=widget;
       switch(m_selectedGraphWidget->getGraphType())
       {
           case GraphECG:
            m_graphSettingsPop->initialized(m_selectedGraphWidget->getGraphWaveSize(),GraphECG);
           break;

           case GraphABP:
            m_graphSettingsPop->initialized(m_selectedGraphWidget->getGraphWaveSize(),GraphABP);
           break;

           case GraphPLETH:
            m_graphSettingsPop->initialized(m_selectedGraphWidget->getGraphWaveSize(),GraphPLETH);
           break;

           case GraphCO2:
                 //TODO
           break;
       }
       m_graphSettingsPop->show();
   }
}

void MainWindow::onGraphMenuPopupOk()
{
    if(m_selectedGraphWidget->getGraphWaveSize()!=m_graphSettingsPop->m_graphWaveSize)
    {
        switch(m_selectedGraphWidget->getGraphType())
        {
            case GraphECG:
                 m_selectedGraphWidget->initialized(GraphECG,m_graphSettingsPop->m_graphWaveSize);
                 m_selectedGraphWidget->repaint();
            break;

            case GraphABP:
                m_selectedGraphWidget->initialized(GraphABP,m_graphSettingsPop->m_graphWaveSize);
                m_selectedGraphWidget->repaint();
            break;

            case GraphPLETH:
                m_selectedGraphWidget->initialized(GraphPLETH,m_graphSettingsPop->m_graphWaveSize);
                m_selectedGraphWidget->repaint();
            break;

            case GraphCO2:
                  //TODO
            break;
        }
    }

    m_graphSettingsPop->hide();
    m_graphSettingsPop->close();
    m_graphSettingsPop->m_isVisible=false;
    m_selectedGraphWidget=NULL;
}

void MainWindow::onGraphMenuPopupCancel()
{
    m_graphSettingsPop->hide();
    m_graphSettingsPop->close();
    m_graphSettingsPop->m_isVisible=false;
    m_selectedGraphWidget=NULL;
}

#ifdef PMD_HAMP

void  MainWindow::takeScreenSnapshort()
{
    QRect r= this->rect();
    QPixmap pixmap=this->grab(r);
    QString fileName = m_hampdataSupplier->getScreenShortPath();

    if(pixmap.isNull()==false)
        pixmap.save(fileName,"PNG");
    else
        printf("\npixmap is NULL");
}

#endif
<commit_msg>test4<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "graphSettingsPopup.h"
#include <QDateTime>

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

#ifdef PMD_HAMP
    ui->pbtn_StartStop->setChecked(true);
#endif
	printf("This is MainWindow\n");
	qDebug("This is MainWindow via QDebug()\n");

    m_alarmBtnNormalStyleSheet=ui->pbtn_ECG_Alarm->styleSheet();
    m_alarmBtnRedStyleSheet= "*{border: 0px;background-image: url(:/images/icnBellRed.png);} *:checked{background-image:url();}";

    m_graphWidget1 = new Widget(ui->graphECGWidget,this);
#ifndef ONLY_SHOW_ECG_GRAPH
    m_graphWidget2 = new Widget(ui->graphABPWidget,this);
    m_graphWidget3 = new Widget(ui->graphPLETHWidget,this);
#endif

    m_graphWidget1->initialized(GraphECG,3);
#ifndef ONLY_SHOW_ECG_GRAPH
    m_graphWidget2->initialized(GraphABP,1);
    m_graphWidget3->initialized(GraphPLETH,2);
#endif

    m_selectedGraphWidget = NULL;

    // Set time
    updateTimeString();

    // Setup timer to update UI
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateTimeString()));
    timer->start(1000);

    m_timerAlarm =new QTimer (this);
    connect(m_timerAlarm, SIGNAL(timeout()), this, SLOT(animateAlarm()));
    m_timerAlarm->setInterval(500);
    m_timerAlarm->stop();

    m_timerDataValues = new QTimer(this);
#ifndef DISABLE_RIGHT_PANEL_NUMERIC_VALUES
    connect(m_timerDataValues, SIGNAL(timeout()), this, SLOT(updatePulse()));
    connect(m_timerDataValues, SIGNAL(timeout()), this, SLOT(updateABP()));
    connect(m_timerDataValues, SIGNAL(timeout()), this, SLOT(updatePLETH()));
    m_timerDataValues->setInterval(3000);
    #ifndef PMD_HAMP
        m_timerDataValues->start();
    #endif
#endif

    m_HB_Simulate_Data << 70 << 68 << 71 << 69<< 67<< 68;
    m_ABP_Higher_Simulate_Data << 100 << 110 << 107 << 124<< 137<< 119;
    m_ABP_Lower_Simulate_Data << 73 << 88 << 77 << 82 << 91 << 79;
    m_PLETH_Simulate_Data << 93 << 96 << 90 << 97 << 95 << 99;

    m_cstatus=true;

#ifdef PMD_MEHV
    m_nserver =new Server(this);
    m_cstatus=false;
#endif

#ifdef PMD_NUCLEUS
    m_dataSupplier=new DataSupplier(this);
#endif

#ifdef PMD_HAMP
	printf("Creating HAMPDataSupplier\n");
    m_hampdataSupplier =new HAMPDataSupplier(this);
    m_cstatus=true;

    // Setup timer to update UI
    QTimer *hamptimer = new QTimer(this);
    connect(hamptimer, SIGNAL(timeout()), this, SLOT(takeScreenSnapshort()));
    hamptimer->start(3000);

#endif

    m_isPauseAll=false;
    m_isStart=true;
    m_isAlarmSilent=false;
    m_btnSilentPressed=false;

    m_graphSettingsPop =new GraphSettingsPopup(this);
    m_graphSettingsPop->hide();

    // Setup graph scrolling mode
#ifdef ENABLE_GRAPH_SCROLLING
    on_pbtn_Scrolling_clicked(true);
    ui->pbtn_Scrolling->setChecked(true);
#else
    on_pbtn_Scrolling_clicked(false);
    ui->pbtn_Scrolling->setChecked(false);
#endif
}

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

void MainWindow::updatePulse()
{
    QString value="0";

    if(m_isPauseAll==true || m_isStart==false)
        return;

    if(m_cstatus)
    {
        value.setNum(this->getPulseValue());
    }

    ui->labelPulse->setText(value);
    ui->labelPulse->repaint();
}

void MainWindow::updateABP()
{

    if(m_isPauseAll==true || m_isStart==false)
        return;

    if(m_cstatus)
    {
      ui->labelABP->setText(this->getABPValue());
    }
    else
        ui->labelABP->setText("0/0");

    ui->labelABP->repaint();
}

void MainWindow::updatePLETH()
{
    if(m_isPauseAll==true || m_isStart==false)
        return;

    if(m_cstatus)
    {
      ui->labelSPO2->setText(this->getPLETHValue());
    }
    else
        ui->labelSPO2->setText("0");

    ui->labelABP->repaint();
}

void MainWindow::updateTimeString()
{
    //get current date and time
    QDateTime dateTime = QDateTime::currentDateTime();
    QString dateTimeString = dateTime.toString("hh:mm:ss AP");
    ui->labelTime->setText(dateTimeString);
    ui->labelTime->repaint();
}

int MainWindow::getPulseValue()
{
    static int index=-1;

    if(index==this->m_HB_Simulate_Data.count()-1)
        index=0;
    else
        index++;

    return m_HB_Simulate_Data[index];
}

QString MainWindow::getABPValue()
{
    static int index=-1;

    if(index==this->m_ABP_Higher_Simulate_Data.count()-1)
        index=0;
    else
        index++;

    QString str=QString("%1/%2").arg(m_ABP_Higher_Simulate_Data[index]).arg(m_ABP_Lower_Simulate_Data[index]);
    return str;
}

QString MainWindow::getPLETHValue()
{
    static int index=-1;

    if(index==this->m_PLETH_Simulate_Data.count()-1)
        index=0;
    else
        index++;

    QString str=QString("%1").arg(m_PLETH_Simulate_Data[index]);
    return str;
}

void MainWindow::on_pbtn_Silent_clicked()
{
   m_btnSilentPressed=!m_btnSilentPressed;
    if(m_btnSilentPressed)
    {
        ui->pbtn_ECG_Alarm->setChecked(true);
        ui->pbtn_ABP_Alarm->setChecked(true);
        ui->pbtn_spo2_Alarm->setChecked(true);
        m_isAlarmSilent=true;
       // ui->pbtn_Silent->setStyleSheet("*{border: 0px;background-image: url(:/images/btn_pressed.png);}");
    }
    else
    {
        ui->pbtn_ECG_Alarm->setChecked(false);
        ui->pbtn_ABP_Alarm->setChecked(false);
        ui->pbtn_spo2_Alarm->setChecked(false);
        m_isAlarmSilent=false;
        //ui->pbtn_Silent->setStyleSheet("");
    }
}

void MainWindow::on_pbtn_StartStop_clicked(bool checked)
{

#ifdef PMD_HAMP

    if(checked)
    {
        m_isStart=false;
        m_timerDataValues->stop();
        ui->pbtn_StartStop->setText("Start");
        m_hampdataSupplier->startStopNucleus(m_isStart);
        m_cstatus=false;
    }
    else
    {
        m_isStart=true;
        ui->pbtn_StartStop->setText("Stop");
        m_timerDataValues->start();
        m_hampdataSupplier->startStopNucleus(m_isStart);
        m_cstatus=true;

    }
   fflush(stdout);
#else

    if(checked)
    {
        m_isStart=false;
        this->m_graphWidget1->clearWidget();
#ifndef ONLY_SHOW_ECG_GRAPH
        this->m_graphWidget2->clearWidget();
        this->m_graphWidget3->clearWidget();
#endif
        ui->pbtn_StartStop->setText("Start");
    }
    else
    {
        m_isStart=true;
        ui->pbtn_StartStop->setText("Stop");
    }

#endif

}

void MainWindow::on_pbtn_PauseAll_clicked(bool checked)
{
    if(!checked)
    {
        m_isPauseAll=false;
        ui->pbtn_PauseAll->setText("Pause All");
    }
    else
    {
        m_isPauseAll=true;
        this->m_graphWidget1->clearWidget();
#ifndef ONLY_SHOW_ECG_GRAPH
        this->m_graphWidget2->clearWidget();
        this->m_graphWidget3->clearWidget();
#endif
        ui->pbtn_PauseAll->setText("Start All");
    }
}

void MainWindow::on_pbtn_Scrolling_clicked(bool checked)
{
 (void)checked;
#ifndef PMD_HAMP
    m_graphWidget1->setScrollingMode(checked);
#ifndef ONLY_SHOW_ECG_GRAPH
    m_graphWidget2->setScrollingMode(checked);
    m_graphWidget3->setScrollingMode(checked);
#endif
#endif
}

void MainWindow::updateTimer()
{
#ifdef PMD_MEHV
    pm_data_struct pm={0,0,0,0};
    dataReceived (&pm);
#endif
}

void MainWindow::animateAlarm()
{
    ui->pbtn_ABP_Alarm->toggle();
    ui->pbtn_ECG_Alarm->toggle();
    ui->pbtn_spo2_Alarm->toggle();
}

void MainWindow::on_pbtn_ECG_Alarm_clicked(bool checked)
{
    (void)checked;
}

void MainWindow::on_pbtn_spo2_Alarm_clicked(bool checked)
{
    (void)checked;
}

void MainWindow::on_pbtn_ABP_Alarm_clicked(bool checked)
{
    (void)checked;
}

void MainWindow::dataReceived(pm_data_struct *current)
{
	if (m_isPauseAll==false)
    {
		switch(m_graphWidget1->getGraphType())
        {

        case GraphECG:
            m_graphWidget1->animate(current->ecgValue);
        break;

        case GraphABP:
            m_graphWidget1->animate(current->abpValue);
        break;

        case GraphPLETH:
            m_graphWidget1->animate(current->plethValue);
        break;

        case GraphCO2:
            //TODO
        break;
        }

#ifndef ONLY_SHOW_ECG_GRAPH
        switch(m_graphWidget2->getGraphType())
        {
        case GraphECG:
            m_graphWidget2->animate(current->ecgValue);
        break;

        case GraphABP:
            m_graphWidget2->animate(current->abpValue);
        break;

        case GraphPLETH:
            m_graphWidget2->animate(current->plethValue);
        break;

        case GraphCO2:
            //TODO
        break;
        }

        switch(m_graphWidget3->getGraphType())
        {
        case GraphECG:
            m_graphWidget3->animate(current->ecgValue);
        break;

        case GraphABP:
            m_graphWidget3->animate(current->abpValue);
        break;

        case GraphPLETH:
            m_graphWidget3->animate(current->plethValue);
        break;

        case GraphCO2:
            //TODO
        break;
        }
#endif
    }
}

#ifndef PMD_NUCLEUS
void MainWindow::connectionStatus(bool status)
{
    QDateTime dateTime = QDateTime::currentDateTime();
    QString dateTimeString = dateTime.toString("hh:mmap");
    m_cstatus=status;

    if(status)
    {
        ui->labelBulletText->setText("External system connected at "+dateTimeString);
        ui->labelBulletText->repaint();
        ui->pbtn_ECG_Alarm->setStyleSheet(m_alarmBtnNormalStyleSheet);
        ui->pbtn_ABP_Alarm->setStyleSheet(m_alarmBtnNormalStyleSheet);
        ui->pbtn_spo2_Alarm->setStyleSheet(m_alarmBtnNormalStyleSheet);
        ui->pbtn_ABP_Alarm->setDisabled(false);
        ui->pbtn_ECG_Alarm->setDisabled(false);
        ui->pbtn_spo2_Alarm->setDisabled(false);
        m_timerAlarm->stop();

    }
    else
    {
        ui->labelBulletText->setText("External system disconnected at "+dateTimeString);
        ui->labelBulletText->repaint();
        ui->pbtn_ECG_Alarm->setStyleSheet(m_alarmBtnRedStyleSheet);
        ui->pbtn_ABP_Alarm->setStyleSheet(m_alarmBtnRedStyleSheet);
        ui->pbtn_spo2_Alarm->setStyleSheet(m_alarmBtnRedStyleSheet);
        ui->pbtn_ABP_Alarm->setDisabled(true);
        ui->pbtn_ECG_Alarm->setDisabled(true);
        ui->pbtn_spo2_Alarm->setDisabled(true);

        m_timerAlarm->start();
        updatePLETH();
        updateABP();
        updatePulse();
    }
}
#endif

void MainWindow::launchGraphMenuPopup(Widget *widget)
{
   if(m_graphSettingsPop->m_isVisible==false)
   {
       m_selectedGraphWidget=widget;
       switch(m_selectedGraphWidget->getGraphType())
       {
           case GraphECG:
            m_graphSettingsPop->initialized(m_selectedGraphWidget->getGraphWaveSize(),GraphECG);
           break;

           case GraphABP:
            m_graphSettingsPop->initialized(m_selectedGraphWidget->getGraphWaveSize(),GraphABP);
           break;

           case GraphPLETH:
            m_graphSettingsPop->initialized(m_selectedGraphWidget->getGraphWaveSize(),GraphPLETH);
           break;

           case GraphCO2:
                 //TODO
           break;
       }
       m_graphSettingsPop->show();
   }
}

void MainWindow::onGraphMenuPopupOk()
{
    if(m_selectedGraphWidget->getGraphWaveSize()!=m_graphSettingsPop->m_graphWaveSize)
    {
        switch(m_selectedGraphWidget->getGraphType())
        {
            case GraphECG:
                 m_selectedGraphWidget->initialized(GraphECG,m_graphSettingsPop->m_graphWaveSize);
                 m_selectedGraphWidget->repaint();
            break;

            case GraphABP:
                m_selectedGraphWidget->initialized(GraphABP,m_graphSettingsPop->m_graphWaveSize);
                m_selectedGraphWidget->repaint();
            break;

            case GraphPLETH:
                m_selectedGraphWidget->initialized(GraphPLETH,m_graphSettingsPop->m_graphWaveSize);
                m_selectedGraphWidget->repaint();
            break;

            case GraphCO2:
                  //TODO
            break;
        }
    }

    m_graphSettingsPop->hide();
    m_graphSettingsPop->close();
    m_graphSettingsPop->m_isVisible=false;
    m_selectedGraphWidget=NULL;
}

void MainWindow::onGraphMenuPopupCancel()
{
    m_graphSettingsPop->hide();
    m_graphSettingsPop->close();
    m_graphSettingsPop->m_isVisible=false;
    m_selectedGraphWidget=NULL;
}

#ifdef PMD_HAMP

void  MainWindow::takeScreenSnapshort()
{
    QRect r= this->rect();
    QPixmap pixmap=this->grab(r);
    QString fileName = m_hampdataSupplier->getScreenShortPath();

	qDebug("MainWindow::takeScreenSnapshort()\n");

    if(pixmap.isNull()==false)
        pixmap.save(fileName,"PNG");
    else
        printf("\npixmap is NULL");
}

#endif
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    // Set up Qt toolbar window
    ui->setupUi(this);
    ui->contourTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    ui->pointEditorPanel->hide();
    ui->frameSlider->setEnabled(false);
    ui->frameSpinBox->setEnabled(false);

    // Set window icon
    QPixmap logo = QPixmap(":/Logo/northern-red.png");
    //setWindowIcon(QIcon(logo));

    // Set up scene
    scene = new QGraphicsScene(this);
    ui->graphicsView->setScene(scene);
    ui->graphicsView->setBackgroundBrush(QBrush(Qt::black, Qt::SolidPattern));
    ui->insetView->setScene(scene);
    ui->insetView->setBackgroundBrush(QBrush(Qt::black, Qt::SolidPattern));

    // Set up frame
    image_item = new ImageItem();
    image_item->setPixmap(logo);
    scene->addItem(image_item);

    // Set up chart
    chart = new Chart;
    chart->setTitle("Dynamic spline chart");
    chart->legend()->hide();
    chart->setAnimationOptions(QChart::AllAnimations);
    chart_view = new QChartView(chart);
    chart_view->setRenderHint(QPainter::Antialiasing);
    chart_view->setFixedSize(300,400);
    ui->chartLayout->addWidget(chart_view);

    // Connect signals
    connect(image_item, SIGNAL(currentPositionRgbChanged(QPointF&)), this, SLOT(showMousePosition(QPointF&)));
    connect(image_item, SIGNAL(pixelClicked(QPointF&)), this, SLOT(onPixelClicked(QPointF&)));
    connect(ui->contourTable, SIGNAL(itemSelectionChanged()), this, SLOT(on_contourTable_itemSelectionChanged()));
    connect(ui->contourTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(on_contourTable_currentCellChanged(int,int,int,int)));
    show();
}

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

void MainWindow::showMousePosition(QPointF &pos)
{
    if (!current_frame.empty())
    {
        cv::Size mat_size = current_frame.size();
        int x = static_cast<int>(pos.x());
        int y = static_cast<int>(pos.y());
        if (x >= 0 && y >= 0 && x <= mat_size.width && y <= mat_size.height)
        {
            ui->mousePositionLabel->setText("x: " + QString::number(x) + ", y: " + QString::number(y));
        }
    }
}

void MainWindow::onPixelClicked(QPointF &pos)
{
    if (!current_frame.empty())
    {
        cv::Size mat_size = current_frame.size();
        int x = static_cast<int>(pos.x());
        int y = static_cast<int>(pos.y());
        if (x >= 0 && y >= 0 && x <= mat_size.width && y <= mat_size.height)
        {
//            int row = ui->frameSlider->value()-1;
////            QString text = QString("%1, %2").arg(x).arg(y);
////            ui->contourTable->setItem(row, 0, new QTableWidgetItem(text));
//            removeAllSceneEllipses();
//            removeAllSceneLines();
//            drawCrosshair(x, y);

            // Update inset
            ui->insetView->centerOn(x,y);
        }
    }
}

void MainWindow::removeAllSceneEllipses()
{
    foreach (QGraphicsItem *item, scene->items())
    {
        QGraphicsEllipseItem *ellipse = qgraphicsitem_cast<QGraphicsEllipseItem *>(item);
        if (ellipse)
        {
            scene->removeItem(ellipse);
        }
    }
}

void MainWindow::removeAllSceneLines()
{
    foreach (QGraphicsItem *item, scene->items())
    {
        QGraphicsLineItem *line = qgraphicsitem_cast<QGraphicsLineItem *>(item);
        if (line)
        {
            scene->removeItem(line);
        }
    }
}

void MainWindow::drawCrosshair(int x, int y, QColor color)
{
    QPen pen = QPen(color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin);
    scene->addEllipse( x-5, y-5, 10, 10, pen);
    scene->addLine(x-4, y, x+4, y, pen);
    scene->addLine(x, y-4, x, y+4, pen);
}

void MainWindow::savePointsToCSV(QString filename)
{
    QString text_data;
    int row_count = ui->contourTable->rowCount();
    text_data += QString("x,y,\n");
    for (int row=0; row<row_count; row++)
    {
        QTableWidgetItem* item = ui->contourTable->item(row, 0);
        if (item)
        {
            QStringList coordinate = item->text().split(",");
            text_data += coordinate[0];
            text_data += ",";
            text_data += coordinate[1];
            text_data += ",";
            text_data += "\n";
        }
    }
    QFile csv_file(filename);
    if(csv_file.open(QIODevice::WriteOnly | QIODevice::Truncate))
    {

        QTextStream out(&csv_file);
        out << text_data;

        csv_file.close();
    }
    qDebug() << "saved all points: " << filename;
}

void MainWindow::drawAllContours(int frame_index, int contour_index)
{
    if (frame_contours.size()>0)
    {
        /// Remove the previous crosshairs
        removeAllSceneEllipses();
        removeAllSceneLines();

        /// Set the frame
        cap.set(CV_CAP_PROP_POS_FRAMES, frame_index);
        cap.read(current_frame);

        /// Draw contours
        cv::RNG rng(12345);
        ContourList contours = frame_contours.at(frame_index);
        Hierarchy hierarchy = frame_hierarchies.at(frame_index);
        std::vector<cv::Point> centroids = frame_centroids.at(frame_index);
        for (int i=0; i<contour_colors.size(); i++)
        {
            cv::Scalar color = contour_colors.at(i);
            if (i==contour_index)
            {
                cv::drawContours(current_frame, contours, i, color, 2, 8, hierarchy, 0, cv::Point());
            } else {
                cv::drawContours(current_frame, contours, i, color, 1, 8, hierarchy, 0, cv::Point());
            }
            cv::Point centroid = centroids.at(i);
            drawCrosshair(centroid.x, centroid.y);
            //drawCrosshair(centroid.x, centroid.y, QColor(color.val[0], color.val[1], color.val[2], 255));
        }

        /// Show in a window
        img = QImage((uchar*) current_frame.data, current_frame.cols, current_frame.rows, current_frame.step, QImage::Format_RGB888);
        QPixmap pixmap;
        pixmap = QPixmap::fromImage(img);

        /// Show in view, scaled to view bounds & keeping aspect ratio
        image_item->setPixmap(pixmap);
    }
}

cv::Point MainWindow::getMeanPoint(const Contour contour)
{
    cv::Point zero(0.0f, 0.0f);
    cv::Point sum  = std::accumulate(contour.begin(), contour.end(), zero);
    cv::Point mean_point = (sum * (1.0f / contour.size()));
    return mean_point;
}

cv::Point MainWindow::getCenterOfMass(const Contour contour)
{
    cv::Moments mu = cv::moments(contour);
    cv::Point centroid = cv::Point(mu.m10/mu.m00 , mu.m01/mu.m00);
    if (centroid.x < 0 || centroid.y < 0)
    {
        return getMeanPoint(contour);
    } else {
        return centroid;
    }
}

void MainWindow::updateAllContours()
{
    /// Clear current contours
    frame_centroids.clear();
    frame_contours.clear();
    frame_hierarchies.clear();
    contour_colors.clear();

    /// Find contours
    int frame_count = cap.get(CV_CAP_PROP_FRAME_COUNT);
    frame_centroids.resize(frame_count);
    frame_contours.resize(frame_count);
    frame_hierarchies.resize(frame_count);

    int thresh = 100;
    cv::RNG rng(12345);
    cv::Mat src_gray;
    cv::Mat canny_output;
    int max_size = 0;
    ContourListSet initial_contours;
    HierarchyListSet initial_hierarchies;
    for (int i=0; i<frame_count; i++)
    {
        cap.set(CV_CAP_PROP_POS_FRAMES, i);
        cap.read(current_frame);

        /// Convert image to gray and blur it
        cv::cvtColor(current_frame, src_gray, CV_BGR2GRAY);
        cv::blur(src_gray, src_gray, cv::Size(3,3));

        /// Detect edges using canny
        cv::Canny(src_gray, canny_output, thresh, thresh*2, 3);

        /// Find contours
        std::vector<cv::Vec4i> hierarchy;
        ContourList contours;
        cv::findContours(canny_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
        initial_contours.push_back(contours);
        initial_hierarchies.push_back(hierarchy);
        if ((int)contours.size() > max_size)
        {
            max_size = contours.size();
        }

        /// Find centroid (mean) of each contour
        for(int j=0; j<(int)contours.size(); j++)
        {
            Contour contour = contours.at(j);
            frame_centroids[i].push_back(getMeanPoint(contour));
        }
    }
    contour_colors.resize(initial_contours.at(0).size());
    qDebug() << "Completed finding contours";

    for (int c=0; c<contour_colors.size(); c++)
    {
        /// Match first contour
        frame_contours.at(0).push_back(initial_contours.at(0).at(c));
        frame_hierarchies.at(0).push_back(initial_hierarchies.at(0).at(c));
        std::vector<cv::Point> first_set = frame_centroids.at(0);
        cv::Point first_point = first_set.at(c);
        for (int i=1; i<(int)frame_count; i++)
        {
            std::vector<cv::Point> point_set = frame_centroids.at(i);
            int best_distance = current_frame.cols;
            int index = -1;
            for (int k=0; k<(int)point_set.size(); k++)
            {
                cv::Point point = point_set.at(k);
                double dist = cv::norm(first_point-point);

                if (dist < best_distance)
                {
                    best_distance = dist;
                    index = k;
                }
            }
            first_point = point_set.at(index);
            frame_contours.at(i).push_back(initial_contours.at(i).at(index));
            frame_hierarchies.at(i).push_back(initial_hierarchies.at(i).at(index));
        }
        /// Set the color for the contour
        cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255));
        contour_colors[c] = (color);

        /// Add first contour to the table
        ui->contourTable->insertRow(ui->contourTable->rowCount());
        ui->contourTable->setItem(ui->contourTable->rowCount()-1, 0, new QTableWidgetItem());
        ui->contourTable->item(ui->contourTable->rowCount()-1, 0)->setBackgroundColor(QColor(color.val[0], color.val[1], color.val[2], 255));
    }
    qDebug() << "Found" << contour_colors.size() << "contours";
}

void MainWindow::on_frameSpinBox_valueChanged(int arg1)
{
    int frame_index = arg1-1;
    int contour_index = ui->contourTable->currentRow();
    drawAllContours(frame_index, contour_index);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    if (!current_frame.empty())
    {
        img = QImage((uchar*) current_frame.data, current_frame.cols, current_frame.rows, current_frame.step, QImage::Format_RGB888);
        QPixmap pixmap = QPixmap::fromImage(img);

        // Show in view, scaled to view bounds & keeping aspect ratio
        image_item->setPixmap(pixmap);
        QRectF bounds = scene->itemsBoundingRect();
        ui->graphicsView->fitInView(bounds, Qt::KeepAspectRatio);
        ui->graphicsView->centerOn(0,0);
    }
}

void MainWindow::on_action_Open_triggered()
{
    /// Load a video
    QString result = QFileDialog::getOpenFileName(this, tr("Select a Video File"), "/home", tr("Video Files (*.avi)"));
    video_filepath = result.toUtf8().constData();
    QString video_filename = QString::fromStdString(video_filepath.substr(video_filepath.find_last_of("/\\") + 1));
    cap = cv::VideoCapture(video_filepath);
    qDebug() << "opened video: " << video_filename;

    /// Enable video control elements, update elements with video information.
    int frame_count = cap.get(CV_CAP_PROP_FRAME_COUNT);
    ui->pointEditorPanel->show();
    ui->videoComboBox->addItem(video_filename);

    /// Get contours
    updateAllContours();

    /// show frame zero
    cap.set(CV_CAP_PROP_POS_FRAMES, 0);
    cap.read(current_frame);
    img = QImage((uchar*) current_frame.data, current_frame.cols, current_frame.rows, current_frame.step, QImage::Format_RGB888);
    QPixmap pixmap = QPixmap::fromImage(img);

    /// Show in view, scaled to view bounds & keeping aspect ratio
    image_item->setPixmap(pixmap);
    QRectF bounds = scene->itemsBoundingRect();
    ui->graphicsView->fitInView(bounds, Qt::KeepAspectRatio);
    ui->graphicsView->centerOn(0,0);

    /// 5X scale in inset view
    ui->insetView->scale(5,5);
    ui->insetView->centerOn(bounds.center());

    /// Set up chart
    chart->axisX()->setRange(1, frame_count);

    /// Enable frame sliders
    ui->frameSlider->setEnabled(true);
    ui->frameSlider->setRange(1, frame_count);
    ui->frameSpinBox->setEnabled(true);
    ui->frameSpinBox->setRange(1, frame_count);
}

void MainWindow::on_contourTable_itemSelectionChanged()
{
    QItemSelectionModel *selection = ui->contourTable->selectionModel();
    ui->deleteContourButton->setEnabled(selection->hasSelection());
}

void MainWindow::on_contourTable_currentCellChanged(int row, int column, int previous_row, int previous_column)
{
    /// Outline the contour selected in the table
    int frame_index = cap.get(CV_CAP_PROP_POS_FRAMES)-1;
    drawAllContours(frame_index, row);
}

void MainWindow::on_deleteContourButton_clicked()
{
    QItemSelectionModel *selection = ui->contourTable->selectionModel();
    int row;
    int shift = 0;
    foreach (QModelIndex index, selection->selectedRows())
    {
        row = index.row() - shift;
        ui->contourTable->removeRow(row);
        /// Erase the contour in each frame
        for (int i=0; i<frame_contours.size(); i++)
        {
            frame_contours[i].erase(frame_contours[i].begin() + row);
            frame_hierarchies[i].erase(frame_hierarchies[i].begin() + row);
        }
        contour_colors.erase(contour_colors.begin() + row);
        shift++;
    }
    on_frameSpinBox_valueChanged(ui->frameSpinBox->value());
}

void MainWindow::on_findContoursButton_clicked()
{
    /// TODO: Check if contours have been updated (deleted) before doing this.
    updateAllContours();
    on_frameSpinBox_valueChanged(ui->frameSpinBox->value());
}
<commit_msg>Added contour, centroid checkboxes.<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    // Set up Qt toolbar window
    ui->setupUi(this);
    ui->contourTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    ui->pointEditorPanel->hide();
    ui->frameSlider->setEnabled(false);
    ui->frameSpinBox->setEnabled(false);

    // Set window icon
    QPixmap logo = QPixmap(":/Logo/northern-red.png");
    //setWindowIcon(QIcon(logo));

    // Set up scene
    scene = new QGraphicsScene(this);
    ui->graphicsView->setScene(scene);
    ui->graphicsView->setBackgroundBrush(QBrush(Qt::black, Qt::SolidPattern));
    ui->insetView->setScene(scene);
    ui->insetView->setBackgroundBrush(QBrush(Qt::black, Qt::SolidPattern));

    // Set up frame
    image_item = new ImageItem();
    image_item->setPixmap(logo);
    scene->addItem(image_item);

    // Set up chart
    chart = new Chart;
    chart->setTitle("Dynamic spline chart");
    chart->legend()->hide();
    chart->setAnimationOptions(QChart::AllAnimations);
    chart_view = new QChartView(chart);
    chart_view->setRenderHint(QPainter::Antialiasing);
    chart_view->setFixedSize(300,400);
    ui->chartLayout->addWidget(chart_view);

    // Connect signals
    connect(image_item, SIGNAL(currentPositionRgbChanged(QPointF&)), this, SLOT(showMousePosition(QPointF&)));
    connect(image_item, SIGNAL(pixelClicked(QPointF&)), this, SLOT(onPixelClicked(QPointF&)));
    connect(ui->contourTable, SIGNAL(itemSelectionChanged()), this, SLOT(on_contourTable_itemSelectionChanged()));
    connect(ui->contourTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(on_contourTable_currentCellChanged(int,int,int,int)));
    show();
}

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

void MainWindow::showMousePosition(QPointF &pos)
{
    if (!current_frame.empty())
    {
        cv::Size mat_size = current_frame.size();
        int x = static_cast<int>(pos.x());
        int y = static_cast<int>(pos.y());
        if (x >= 0 && y >= 0 && x <= mat_size.width && y <= mat_size.height)
        {
            ui->mousePositionLabel->setText("x: " + QString::number(x) + ", y: " + QString::number(y));
        }
    }
}

void MainWindow::onPixelClicked(QPointF &pos)
{
    if (!current_frame.empty())
    {
        cv::Size mat_size = current_frame.size();
        int x = static_cast<int>(pos.x());
        int y = static_cast<int>(pos.y());
        if (x >= 0 && y >= 0 && x <= mat_size.width && y <= mat_size.height)
        {
//            int row = ui->frameSlider->value()-1;
////            QString text = QString("%1, %2").arg(x).arg(y);
////            ui->contourTable->setItem(row, 0, new QTableWidgetItem(text));
//            removeAllSceneEllipses();
//            removeAllSceneLines();
//            drawCrosshair(x, y);

            // Update inset
            ui->insetView->centerOn(x,y);
        }
    }
}

void MainWindow::removeAllSceneEllipses()
{
    foreach (QGraphicsItem *item, scene->items())
    {
        QGraphicsEllipseItem *ellipse = qgraphicsitem_cast<QGraphicsEllipseItem *>(item);
        if (ellipse)
        {
            scene->removeItem(ellipse);
        }
    }
}

void MainWindow::removeAllSceneLines()
{
    foreach (QGraphicsItem *item, scene->items())
    {
        QGraphicsLineItem *line = qgraphicsitem_cast<QGraphicsLineItem *>(item);
        if (line)
        {
            scene->removeItem(line);
        }
    }
}

void MainWindow::drawCrosshair(int x, int y, QColor color)
{
    QPen pen = QPen(color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin);
    scene->addEllipse( x-5, y-5, 10, 10, pen);
    scene->addLine(x-4, y, x+4, y, pen);
    scene->addLine(x, y-4, x, y+4, pen);
}

void MainWindow::savePointsToCSV(QString filename)
{
    QString text_data;
    int row_count = ui->contourTable->rowCount();
    text_data += QString("x,y,\n");
    for (int row=0; row<row_count; row++)
    {
        QTableWidgetItem* item = ui->contourTable->item(row, 0);
        if (item)
        {
            QStringList coordinate = item->text().split(",");
            text_data += coordinate[0];
            text_data += ",";
            text_data += coordinate[1];
            text_data += ",";
            text_data += "\n";
        }
    }
    QFile csv_file(filename);
    if(csv_file.open(QIODevice::WriteOnly | QIODevice::Truncate))
    {

        QTextStream out(&csv_file);
        out << text_data;

        csv_file.close();
    }
    qDebug() << "saved all points: " << filename;
}

void MainWindow::drawAllContours(int frame_index, int contour_index)
{
    if (frame_contours.size()>0)
    {
        /// Remove the previous crosshairs
        removeAllSceneEllipses();
        removeAllSceneLines();

        /// Set the frame
        cap.set(CV_CAP_PROP_POS_FRAMES, frame_index);
        cap.read(current_frame);

        /// Draw contours
        if (ui->contoursCheckBox->isChecked())
        {
            cv::RNG rng(12345);
            ContourList contours = frame_contours.at(frame_index);
            Hierarchy hierarchy = frame_hierarchies.at(frame_index);
            for (int i=0; i<contour_colors.size(); i++)
            {
                cv::Scalar color = contour_colors.at(i);
                if (i==contour_index)
                {
                    cv::drawContours(current_frame, contours, i, color, 2, 8, hierarchy, 0, cv::Point());
                } else {
                    cv::drawContours(current_frame, contours, i, color, 1, 8, hierarchy, 0, cv::Point());
                }

            }
        }

        /// Draw centroids
        if (ui->centroidsCheckBox->isChecked())
        {
            std::vector<cv::Point> centroids = frame_centroids.at(frame_index);
            for (int i=0; i<centroids.size(); i++)
            {
                cv::Point centroid = centroids.at(i);
                drawCrosshair(centroid.x, centroid.y);
            }
        }

        /// Show in a window
        img = QImage((uchar*) current_frame.data, current_frame.cols, current_frame.rows, current_frame.step, QImage::Format_RGB888);
        QPixmap pixmap;
        pixmap = QPixmap::fromImage(img);

        /// Show in view, scaled to view bounds & keeping aspect ratio
        image_item->setPixmap(pixmap);
    }
}

cv::Point MainWindow::getMeanPoint(const Contour contour)
{
    cv::Point zero(0.0f, 0.0f);
    cv::Point sum  = std::accumulate(contour.begin(), contour.end(), zero);
    cv::Point mean_point = (sum * (1.0f / contour.size()));
    return mean_point;
}

cv::Point MainWindow::getCenterOfMass(const Contour contour)
{
    cv::Moments mu = cv::moments(contour);
    cv::Point centroid = cv::Point(mu.m10/mu.m00 , mu.m01/mu.m00);
    if (centroid.x < 0 || centroid.y < 0)
    {
        return getMeanPoint(contour);
    } else {
        return centroid;
    }
}

void MainWindow::updateAllContours()
{
    /// Clear current contours
    frame_centroids.clear();
    frame_contours.clear();
    frame_hierarchies.clear();
    contour_colors.clear();

    /// Find contours
    int frame_count = cap.get(CV_CAP_PROP_FRAME_COUNT);
    frame_centroids.resize(frame_count);
    frame_contours.resize(frame_count);
    frame_hierarchies.resize(frame_count);

    int thresh = 100;
    cv::RNG rng(12345);
    cv::Mat src_gray;
    cv::Mat canny_output;
    int max_size = 0;
    ContourListSet initial_contours;
    HierarchyListSet initial_hierarchies;
    for (int i=0; i<frame_count; i++)
    {
        cap.set(CV_CAP_PROP_POS_FRAMES, i);
        cap.read(current_frame);

        /// Convert image to gray and blur it
        cv::cvtColor(current_frame, src_gray, CV_BGR2GRAY);
        cv::blur(src_gray, src_gray, cv::Size(3,3));

        /// Detect edges using canny
        cv::Canny(src_gray, canny_output, thresh, thresh*2, 3);

        /// Find contours
        std::vector<cv::Vec4i> hierarchy;
        ContourList contours;
        cv::findContours(canny_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
        initial_contours.push_back(contours);
        initial_hierarchies.push_back(hierarchy);
        if ((int)contours.size() > max_size)
        {
            max_size = contours.size();
        }

        /// Find centroid (mean) of each contour
        for(int j=0; j<(int)contours.size(); j++)
        {
            Contour contour = contours.at(j);
            frame_centroids[i].push_back(getMeanPoint(contour));
        }
    }
    contour_colors.resize(initial_contours.at(0).size());
    qDebug() << "Completed finding contours";

    for (int c=0; c<contour_colors.size(); c++)
    {
        /// Match first contour
        frame_contours.at(0).push_back(initial_contours.at(0).at(c));
        frame_hierarchies.at(0).push_back(initial_hierarchies.at(0).at(c));
        std::vector<cv::Point> first_set = frame_centroids.at(0);
        cv::Point first_point = first_set.at(c);
        for (int i=1; i<(int)frame_count; i++)
        {
            std::vector<cv::Point> point_set = frame_centroids.at(i);
            int best_distance = current_frame.cols;
            int index = -1;
            for (int k=0; k<(int)point_set.size(); k++)
            {
                cv::Point point = point_set.at(k);
                double dist = cv::norm(first_point-point);

                if (dist < best_distance)
                {
                    best_distance = dist;
                    index = k;
                }
            }
            first_point = point_set.at(index);
            frame_contours.at(i).push_back(initial_contours.at(i).at(index));
            frame_hierarchies.at(i).push_back(initial_hierarchies.at(i).at(index));
        }
        /// Set the color for the contour
        cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255));
        contour_colors[c] = (color);

        /// Add first contour to the table
        ui->contourTable->insertRow(ui->contourTable->rowCount());
        ui->contourTable->setItem(ui->contourTable->rowCount()-1, 0, new QTableWidgetItem());
        ui->contourTable->item(ui->contourTable->rowCount()-1, 0)->setBackgroundColor(QColor(color.val[0], color.val[1], color.val[2], 255));
    }
    qDebug() << "Found" << contour_colors.size() << "contours";
}

void MainWindow::on_frameSpinBox_valueChanged(int arg1)
{
    int frame_index = arg1-1;
    int contour_index = ui->contourTable->currentRow();
    drawAllContours(frame_index, contour_index);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    if (!current_frame.empty())
    {
        img = QImage((uchar*) current_frame.data, current_frame.cols, current_frame.rows, current_frame.step, QImage::Format_RGB888);
        QPixmap pixmap = QPixmap::fromImage(img);

        // Show in view, scaled to view bounds & keeping aspect ratio
        image_item->setPixmap(pixmap);
        QRectF bounds = scene->itemsBoundingRect();
        ui->graphicsView->fitInView(bounds, Qt::KeepAspectRatio);
        ui->graphicsView->centerOn(0,0);
    }
}

void MainWindow::on_action_Open_triggered()
{
    /// Load a video
    QString result = QFileDialog::getOpenFileName(this, tr("Select a Video File"), "/home", tr("Video Files (*.avi)"));
    video_filepath = result.toUtf8().constData();
    QString video_filename = QString::fromStdString(video_filepath.substr(video_filepath.find_last_of("/\\") + 1));
    cap = cv::VideoCapture(video_filepath);
    qDebug() << "opened video: " << video_filename;

    /// Enable video control elements, update elements with video information.
    int frame_count = cap.get(CV_CAP_PROP_FRAME_COUNT);
    ui->pointEditorPanel->show();
    ui->videoComboBox->addItem(video_filename);

    /// Get contours
    updateAllContours();

    /// show frame zero
    cap.set(CV_CAP_PROP_POS_FRAMES, 0);
    cap.read(current_frame);
    img = QImage((uchar*) current_frame.data, current_frame.cols, current_frame.rows, current_frame.step, QImage::Format_RGB888);
    QPixmap pixmap = QPixmap::fromImage(img);

    /// Show in view, scaled to view bounds & keeping aspect ratio
    image_item->setPixmap(pixmap);
    QRectF bounds = scene->itemsBoundingRect();
    ui->graphicsView->fitInView(bounds, Qt::KeepAspectRatio);
    ui->graphicsView->centerOn(0,0);

    /// 5X scale in inset view
    ui->insetView->scale(5,5);
    ui->insetView->centerOn(bounds.center());

    /// Set up chart
    chart->axisX()->setRange(1, frame_count);

    /// Enable frame sliders
    ui->frameSlider->setEnabled(true);
    ui->frameSlider->setRange(1, frame_count);
    ui->frameSpinBox->setEnabled(true);
    ui->frameSpinBox->setRange(1, frame_count);
}

void MainWindow::on_contourTable_itemSelectionChanged()
{
    QItemSelectionModel *selection = ui->contourTable->selectionModel();
    ui->deleteContourButton->setEnabled(selection->hasSelection());
}

void MainWindow::on_contourTable_currentCellChanged(int row, int column, int previous_row, int previous_column)
{
    /// Outline the contour selected in the table
    int frame_index = cap.get(CV_CAP_PROP_POS_FRAMES)-1;
    drawAllContours(frame_index, row);
}

void MainWindow::on_deleteContourButton_clicked()
{
    QItemSelectionModel *selection = ui->contourTable->selectionModel();
    int row;
    int shift = 0;
    foreach (QModelIndex index, selection->selectedRows())
    {
        row = index.row() - shift;
        ui->contourTable->removeRow(row);
        /// Erase the contour in each frame
        for (int i=0; i<frame_contours.size(); i++)
        {
            frame_contours[i].erase(frame_contours[i].begin() + row);
            frame_hierarchies[i].erase(frame_hierarchies[i].begin() + row);
        }
        contour_colors.erase(contour_colors.begin() + row);
        shift++;
    }
    on_frameSpinBox_valueChanged(ui->frameSpinBox->value());
}

void MainWindow::on_findContoursButton_clicked()
{
    /// TODO: Check if contours have been updated (deleted) before doing this.
    updateAllContours();
    on_frameSpinBox_valueChanged(ui->frameSpinBox->value());
}
<|endoftext|>