728x90

enum 값을 사용할 때 for 문으로 enum 값을 증가시키면서 사용하는 방식을 사용할 수도 있습니다. 이럴 때 enum을 정의한 후 바로 증가 연산자를 사용할 수 없고 operator++를 재정의 해주어야 합니다.

enum fruit {
	frApple,
    frOrange,
    
    num_of_fruit
};

fruit& operator++(fruit& fr) {
    fr = static_cast<fruit>(static_cast<int>(fr) + 1);
    return fr;
}

fruit operator++(fruit& fr, int) {
    fruit temp = fr;
    fr = static_cast<fruit>(static_cast<int>(fr) + 1);
    if (fr > num_of_fruit) {
        fr = num_of_fruit;  
    }
    return temp;
}

첫 번째 operator++는 전위 증가 연산자를 위한 재정의, 두 번째 operator++는 후위 증가 연산자를 위한 재정의 함수입니다.

 

728x90

'Programming > C++' 카테고리의 다른 글

[C++] boost windows 비동기 처리하기  (2) 2023.10.23
[C++] Template(2)  (0) 2023.09.22
[C++] Template(1)  (2) 2023.09.19
[C++] std::forward_list, std::list  (0) 2023.07.18
[C++] std::array, std::vector  (0) 2023.07.12
728x90

보통 QTreeWidget에서 아이템을 싹 비우려고 할 때 Clear() 함수를 많이 사용합니다. 하지만 이 함수의 경우 QTreeWidget에 가지고 있는 아이템들의 메모리를 해제시키기 때문에 아래와 같이 아이템을 다시 불러오려고 할 때 문제가 생기게 됩니다.

class Test {
    Test(uchar nItemCount) {
    	for ( int i=0; i < nItemCount; i++ ) {
    		QTreeWidgetItem* it = new QTreeWidgetItem;
        	items.append(it);
        }
    }
    QList<QTreeWidgetItem*> getItems() { return Items; }
    
	QList<QTreeWidgetItem*> Items;
};

int main() {
    Test* t1 = new Test(3);
    Test* t2 = new Test(10);
    
    auto items = t1->getItems();
    for ( auto i : items ) {
    	ui->tree->addTopLevelCount(i);
    }
    
    ui->tree->clear();		// t1이 갖고 있는 item들 delete 됨
    
    auto items = t2->getItems();
    for ( auto i : items ) {
    	ui->tree->addTopLevelCount(i);
    }
    
    ui->tree->clear();		// t2이 갖고 있는 item들 delete 됨
    
    auto items = t1->getItems();
    for ( auto i : items ) {
    	ui->tree->addTopLevelCount(i);		// i는 쓰레기 값
    }
    
    ...
}

이럴때는 clear를 사용하는 것이 아닌 takeTopLevelItem을 통해 단순히 트리에서 제거해주면 됩니다.

while (topLevelItemCount() > 0) {
    takeTopLevelItem(0);
} // clear but not delete items
728x90
728x90

엑셀을 잘 사용하다가 갑자기 아래 사진과 같이 파란색 굵은 점선이 보이는 경우가 있을 수 있습니다.


이럴때 [보기] > [페이지 나누기 미리 보기]를 [기본]으로 선택하면 파란 선은 나오지 않습니다.



하지만 이렇게 해도 점선은 보이게 되는데 이 영역은 결국 페이지 미리보기 즉 인쇄됐을 때 한 페이지 당 출력되는 내용을 미리 볼 수 있는 건데 이렇게 작은 단위로 잘게 쪼개져 있다면 용지 크기를 한번 확인해 볼 필요가 있습니다.
 
확인해보니 A4와 같은 일반적인 페이지 크기가 아닌 다른 유형이 있는 것을 볼 수 있습니다.



이 경우는 라벨 프린터기나 다른 프린터 제품을 사용할 때 볼 수 있는데 이럴때는 [파일] > [인쇄]를 클릭해서 프린터를 변경해보시면 됩니다.


 
변경이 완료됐다면 [보기] > [페이지 나누기 미리 보기]를 선택해도 정상적으로 보이게 됩니다.

 

728x90
728x90

예전 포스팅에서 소개했던 QtConcurrent를 사용하면 간단하고 Qt Event에 특화된 비동기 처리가 가능합니다. 이 QtConcorruent에서 제공하는 map-reduce 패턴을 사용할 수 있습니다.

 

map-reduce 패턴을 간단히 설명하자면 수행할 작업을 분산하여 처리하고 하나로 모은다고 생각하시면 됩니다.

QtConcurrent::mappedReuced 함수 원형입니다.

template <typename Sequence, typename MapFunction, typename ReduceFunction>
QFuture<typename QtPrivate::MapResultType<MapFunction, Sequence>::ResultType>
mappedReduced(const Sequence &sequence, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions options = QtConcurrent::SequentialReduce);

 

전달되는 파라미터에 대한 설명입니다.

  • sequence : 처리할 데이터 셋(QList, QVector)
  • mapFunction : 데이터가 처리될 함수 전달
  • reduceFunction : 각각 분산되어 처리된 데이터를 하나로 모으는 함수 전달

반환 값은 QFuture 데이터가 반환되며 비동기 상태를 관리하는 객체입니다. waitForResult 함수로 처리가 끝날때까지 대기가 가능하며 result 함수로 처리 결과를 받을 수 있습니다.

 

아래 코드는 QtConcurrent::mappedReduced를 사용하는 간단한 예제입니다. 

먼저 .pro에 concurrent 모듈을 추가합니다.

QT = core concurrent

전달되는 Function을 람다로 처리하였습니다.

#include <QtConcurrent>
#include <QFuture>
#include <QList>
#include <iostream>


int main() {
    QList<int> data;
    for ( int i=0; i<= 100; i++ ) {
        data.append(i);
    }

    auto mapFunction = [&] ( int value ) -> int {
        qDebug() << "Map : " << "value = " << value;
        return value * 2;
    };

    auto reduceFunction = [&] ( int &result, int value ) {
        result += value;
        qDebug() << "Reduce : " << "Origin Value = " << value / 2 << "Value = " << value << " Result = " << result;
    };

    // mappedReduced 호출
    QFuture<int> future = QtConcurrent::mappedReduced(data, mapFunction, reduceFunction, QtConcurrent::SequentialReduce);

    // 작업 완료 대기 및 결과 출력
    future.waitForFinished();
    int result = future.result();
    std::cout << "Result: " << result << std::endl; // 출력 예시: 30

    return 0;
}
// 실행 결과
Map :  value =  0
Map :  value =  1
Map :  value =  2
Map :  value =  3
Map :  value =  4
Map :  value =  5
Map :  value =  7
Map :  value =  6
Map :  value =  8
Map :  value =  10
Map :  value =  9
/* 중략 */
Reduce :  Origin Value =  91 Value =  182  Result =  8372
Reduce :  Origin Value =  92 Value =  184  Result =  8556
Reduce :  Origin Value =  93 Value =  186  Result =  8742
Reduce :  Origin Value =  94 Value =  188  Result =  8930
Reduce :  Origin Value =  95 Value =  190  Result =  9120
Reduce :  Origin Value =  96 Value =  192  Result =  9312
Reduce :  Origin Value =  97 Value =  194  Result =  9506
Reduce :  Origin Value =  98 Value =  196  Result =  9702
Reduce :  Origin Value =  99 Value =  198  Result =  9900
Reduce :  Origin Value =  100 Value =  200  Result =  10100
Result: 10100

 

728x90
728x90

QTreeWidget에 Data를 추가 한 후 Json 형태로 Save 및 Load 하는 방법입니다.
 
먼저 좌측에 간단한 데이터가 입력된 Tree가 있고 Save로 Data를 Json 형태로 저장 후 Load 시 우측의 Tree에 Json Data를 출력하는 간단한 예제입니다.
 
아래는 코드 입니다. Save 버튼을 클릭했을 때  Tree의 정보를 Json형태로 Save 하고 Load 버튼을 클릭했을 때 Json을 읽어서 Tree에 Item을 생성하여 넣어줍니다.

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

    connect ( ui->btnSave, &QPushButton::clicked, this, [&] {
        QJsonObject saveData;           // 전체 Json 저장할 Object 객체

        QFile saveFile("SaveFile.json");
        if ( !saveFile.open(QIODevice::WriteOnly) ) {
            return;
        }

        QJsonArray TopArray;        // Top Item을 저장할 Array 객체
        for ( int i = 0; i < ui->saveTree->topLevelItemCount(); i++ ) {
            QJsonObject TopObject;

            auto TopItem = ui->saveTree->topLevelItem(i);
            TopObject.insert("Name", TopItem->text(0));

            QJsonArray ChildArray;      // Child Item을 저장할 Array 객체
            for ( int c = 0; c < TopItem->childCount(); c++ ) {
                QJsonObject ChildObject;

                auto ChildItem = TopItem->child(c);
                ChildObject.insert("Name", ChildItem->text(0));
                ChildObject.insert("Value", ChildItem->text(1));
                ChildArray.append(ChildObject);
            }

            TopObject.insert("Child", ChildArray);
            TopArray.append(TopObject);
        }

        saveData.insert("Top", TopArray);

        saveFile.write(QJsonDocument(saveData).toJson());
        saveFile.close();
    });

    connect ( ui->btnLoad, &QPushButton::clicked, this, [&] {
        QFile loadFile("SaveFile.json");
        if ( !loadFile.open(QIODevice::ReadOnly) ) {
            return;
        }

        QJsonDocument loadDocument = QJsonDocument::fromJson(loadFile.readAll());

        auto TopData = loadDocument.object().value("Top").toArray();        // Top Array 정보를 불러옴
        for ( auto topIt = TopData.cbegin(); topIt != TopData.cend(); ++topIt ) {
            auto top = (*topIt).toObject();
            auto makeItem = [&](QJsonObject& topJson) -> QTreeWidgetItem* {
                QTreeWidgetItem* topItem = new QTreeWidgetItem;

                topItem->setText(0, topJson.value("Name").toString());

                auto Child = topJson.value("Child").toArray();      // Top 하위의 Child Array 정보를 불러옴
                for ( auto childIt = Child.cbegin(); childIt != Child.cend(); ++childIt ) {
                    auto childObject = (*childIt).toObject();

                    QTreeWidgetItem* childItem = new QTreeWidgetItem(topItem);
                    childItem->setText(0, childObject.value("Name").toString());
                    childItem->setText(1, childObject.value("Value").toString());
                }

                return topItem;
            }; // Top 정보를 가지고 Item 생성해주는 람다 함수

            ui->loadTree->addTopLevelItem(makeItem(top));
        }

        loadFile.close();
    });
}
{
    "Top": [
        {
            "Child": [
                {
                    "Name": "Ten",
                    "Value": "10"
                },
                {
                    "Name": "Eleven",
                    "Value": "11"
                }
            ],
            "Name": "One"
        },
        {
            "Child": [
            ],
            "Name": "Two"
        }
    ]
}

 

728x90
728x90

기본적으로 QTimer를 만들고 timeout Singal을 connect 해준다면 Main Thread에서 Slot이 처리됩니다. 이럴 때 QThread를 생성 후 Timer를 그 쓰레드로 이동시켜 사용한다면 Main Thread가 아닌 생성한 Thread에서 처리가 됩니다.

class timerMgr : public QObject {
    Q_OBJECT
public:
    timerMgr() {
        for ( int i=0; i < 3 ; i++ ) {
            QTimer* t = new QTimer;
            t->setObjectName("Timer" + QString::number(i));
            t->setInterval(QRandomGenerator::global()->bounded(100, 2000));
            connect ( t, &QTimer::timeout, this, &timerMgr::printTimer);
            timerList.append(t);
        } // 랜덤 주기의 Timer를 생성하고 Connect
    }

    void startTimer() {
        for ( auto t : timerList ) {
            t->start();
        }
    }

private slots:
    void printTimer() {
        qDebug() << "Timeout Thread = " << QThread::currentThreadId();
    } // Thread ID 출력

private:
    QList<QTimer*> timerList;
};


QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    qDebug() << "Main Thread = " << QThread::currentThreadId();
    QThread* TimerThread = new QThread;		// Thread 생성
    timerMgr* t = new timerMgr;	
    t->moveToThread(TimerThread);			// Timer Manager 클래스를 TimerThread에서 돌게
    TimerThread->start();					// Timer Thread Start

    t->startTimer();
}

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

위 코드를 실행하면 아래와 같이 Timeout에 대한 Slot 함수가 다른 Thread에서 발생했다는 것을 볼 수 있습니다.

더보기

실행결과

 

Main Thread =  0x52f8
Timeout Thread =  0x638c
Timeout Thread =  0x638c
Timeout Thread =  0x638c
Timeout Thread =  0x638c
Timeout Thread =  0x638c
Timeout Thread =  0x638c
Timeout Thread =  0x638c
Timeout Thread =  0x638c

728x90
728x90
void clearOnlyItem(QTableWidget* table)
{
	table->setRowCount(0);
}

 

728x90
728x90

Windows / PCAN USB를 가지고 ISOTP 혹은 CAN-FD로 간단하게 채팅할 수 있는 프로그램을 제작해 보았습니다. PCAN API C++ 관련 자료가 많이 없어 이 API를 가지고 개발하시려는 분들께 도움이 됐으면 좋겠습니다.

 

 

GitHub - psy1064/PCAN-Chat

Contribute to psy1064/PCAN-Chat development by creating an account on GitHub.

github.com

 

728x90

+ Recent posts