This commit is contained in:
seo-amugae committed 2026-02-24 18:30:51 +09:00
1 parent e429161a89
commit 0c55b8f1aa
134 files changed
+6071 -5801

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
QT += core gui network serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
SOURCES += \
barcodescanner.cpp \
main.cpp \
mainwidget.cpp \
pagecart.cpp \
pageguide.cpp \
pagepay.cpp \
pagepay_card.cpp \
pagetotalpay.cpp \
pagewelcome.cpp
HEADERS += \
barcodescanner.h \
mainwidget.h \
pagecart.h \
pageguide.h \
pagepay.h \
pagepay_card.h \
pagetotalpay.h \
pagewelcome.h
FORMS += \
mainwidget.ui \
pagecart.ui \
pageguide.ui \
pagepay.ui \
pagepay_card.ui \
pagetotalpay.ui \
pagewelcome.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES +=
RESOURCES += \
etc_image/etc_image.qrc \
item_image/item_image.qrc
+108
View File
@@ -0,0 +1,108 @@
#include "barcodescanner.h"
#include <QNetworkRequest>
#include <QUrl>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVariant>
BarcodeScanner::BarcodeScanner(QObject *parent)
: QObject(parent),
manager(new QNetworkAccessManager(this))
{
connect(manager, &QNetworkAccessManager::finished,
this, &BarcodeScanner::onNetworkReply);
}
void BarcodeScanner::setApiBaseUrl(const QString &ip, int port)
{
m_apiBaseUrl = QString("http://%1:%2").arg(ip).arg(port);
qDebug() << "[Scanner] API Base URL set to:" << m_apiBaseUrl;
}
// 상품 추가
void BarcodeScanner::fetchItemDetails(const QString& itemId)
{
QString path = QString("%1/cart/add/%2").arg(m_apiBaseUrl).arg(itemId);
QUrl url(path);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
manager->post(request, QByteArray());
qDebug() << "==================================================";
qDebug() << "[Scanner] Requesting ADD -> ID:" << itemId;
qDebug() << "[Scanner] URL:" << url.toString();
}
// 상품 제거
void BarcodeScanner::removeItem(const QString& itemId)
{
QString path = QString("%1/cart/remove/%2").arg(m_apiBaseUrl).arg(itemId);
QUrl url(path);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
manager->post(request, QByteArray());
qDebug() << "==================================================";
qDebug() << "[Scanner] Requesting REMOVE -> ID:" << itemId;
qDebug() << "[Scanner] URL:" << url.toString();
}
void BarcodeScanner::onNetworkReply(QNetworkReply *reply)
{
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "[Scanner] Network Error:" << reply->errorString();
emit fetchFailed(reply->errorString());
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
qDebug() << "[Scanner] Raw Response Data:" << data;
QJsonDocument doc = QJsonDocument::fromJson(data);
if (!doc.isObject()) {
qDebug() << "[Scanner] Invalid JSON response";
reply->deleteLater();
return;
}
QJsonObject obj = doc.object();
QString action = obj["action"].toString();
qDebug() << "[Scanner] Parsed Action:" << action;
// 데이터 유효성 검사
if (action == "add") {
Item item;
if (obj.contains("item")) item.name = obj["item"].toString();
else if (obj.contains("name")) item.name = obj["name"].toString();
else item.name = "알수없음";
if (obj.contains("id")) {
item.id = obj["id"].toVariant().toString();
}
if (obj.contains("price")) item.price = obj["price"].toDouble();
double expectedWeight = 0.0;
if (obj.contains("expected_weight")) {
expectedWeight = obj["expected_weight"].toDouble();
item.weight = expectedWeight;
}
qDebug() << "[Scanner] Item Parsed: " << item.name << ", " << item.price;
emit itemFetched(item, expectedWeight);
}
else if (action == "remove") {
qDebug() << "[Scanner] Remove confirmed.";
}
reply->deleteLater();
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef BARCODESCANNER_H
#define BARCODESCANNER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QString>
class Item {
public:
QString id = "";
QString name = "";
double price = 0.0;
double weight = 0.0;
int stock = 0;
};
class BarcodeScanner : public QObject
{
Q_OBJECT
public:
explicit BarcodeScanner(QObject *parent = nullptr);
void setApiBaseUrl(const QString &ip, int port);
void fetchItemDetails(const QString& itemId);
void removeItem(const QString& itemId);
signals:
void itemFetched(const Item &item, double cartWeight);
void fetchFailed(const QString &error);
private slots:
void onNetworkReply(QNetworkReply *reply);
private:
QNetworkAccessManager *manager;
QString m_apiBaseUrl;
};
#endif // BARCODESCANNER_H
+1
View File
@@ -0,0 +1 @@
<RCC/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

+37
View File
@@ -0,0 +1,37 @@
<RCC>
<qresource prefix="/etc">
<file>card.png</file>
<file>cart.png</file>
<file>counter.png</file>
<file>food.png</file>
<file>home.png</file>
<file>kitty.jpg</file>
<file>search.png</file>
<file>wind.png</file>
<file>map.pgm</file>
<file>guide_map.png</file>
<file>map_handcream.png</file>
<file>map_phone.png</file>
<file>mappin.png</file>
<file>map_snack.png</file>
<file>map_toy.png</file>
<file>map_pay.png</file>
<file>map_cream.png</file>
<file>map_toy_2.png</file>
<file>map_phone_1.png</file>
<file>map_door.png</file>
</qresource>
<qresource prefix="/">
<file>cart_puzzle.png</file>
<file>cart_snack(1).jpg</file>
<file>cart_snack(2).png</file>
<file>cart_snack(3).jpg</file>
<file>cart_snack(4).jpg</file>
<file>cart_snack(5).jpg</file>
<file>cart_snack(6).jpg</file>
<file>cart_snack(7).jpg</file>
<file>cart_snack(8).jpg</file>
<file>cart_handcream.jpg</file>
<file>cart_iphone.jpg</file>
</qresource>
</RCC>
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

+10
View File
@@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/item">
<file>apple.jpg</file>
<file>banana.jpg</file>
<file>cart_handcream.jpg</file>
<file>cart_iphone.jpg</file>
<file>cart_snack.jpg</file>
<file>fire.jpg</file>
</qresource>
</RCC>
+28
View File
@@ -0,0 +1,28 @@
#include "mainwidget.h"
#include <QApplication>
#include <QScreen>
int main(int argc, char *argv[])
{
// (선택) DPI 흔들림 줄이기 — QApplication 생성 전에
QCoreApplication::setAttribute(Qt::AA_Use96Dpi);
QApplication a(argc, argv);
// (선택) 스타일/폰트 통일
QApplication::setStyle("Fusion");
// QApplication::setFont(QFont("Noto Sans CJK KR", 10));
MainWidget w;
// ✅ 800x480 고정
w.setFixedSize(800, 480);
// ✅ 타이틀바까지 꽉 채우고 싶으면 FullScreen
w.showFullScreen();
// (대안) 창모드로 크게만: w.show();
// (대안) 최대화: w.showMaximized();
return a.exec();
}
+281
View File
@@ -0,0 +1,281 @@
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include <QDebug>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QNetworkDatagram>
#include <QApplication>
#include <QEvent>
#include <QKeyEvent>
#include <QMessageBox>
#include <QUrl>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
, m_isCheckingWeight(false)
{
ui->setupUi(this);
qApp->installEventFilter(this);
m_udpSocket = new QUdpSocket(this);
m_udpSocket->bind(QHostAddress::Any, QT_LISTEN_PORT, QUdpSocket::ShareAddress);
connect(m_udpSocket, &QUdpSocket::readyRead, this, &MainWidget::onUdpReadyRead);
m_networkManager = new QNetworkAccessManager(this);
connect(m_networkManager, &QNetworkAccessManager::finished, this, &MainWidget::onCheckWeightResponse);
pPageWelcome = new PageWelcome(this);
pPageCart = new PageCart(this);
pPageCart->setApiConfig(API_SERVER_IP, API_SERVER_PORT);
pPageGuide = new PageGuide(this);
pPagePay = new PagePay(this);
pPageCard = new pagepay_card(this);
pPageTotalPay= new PageTotalPay(this);
ui->pstackedWidget->addWidget(pPageWelcome);
ui->pstackedWidget->addWidget(pPageCart);
ui->pstackedWidget->addWidget(pPageGuide);
ui->pstackedWidget->addWidget(pPagePay);
ui->pstackedWidget->addWidget(pPageCard);
ui->pstackedWidget->addWidget(pPageTotalPay);
ui->pstackedWidget->setCurrentWidget(pPageWelcome);
connect(pPageWelcome, &PageWelcome::startRequested, this, &MainWidget::slotShowCartPage);
connect(pPageGuide, &PageGuide::requestGoal, this, &MainWidget::onGoalRequestReceived);
connect(pPageGuide, &PageGuide::requestStop, this, &MainWidget::onStopRequestReceived);
connect(this, SIGNAL(robotArrivedSignal()), pPageGuide, SLOT(onRobotArrived()));
connect(pPageGuide, SIGNAL(backToCartClicked()), this, SLOT(slotShowCartPage()));
connect(pPageCart, SIGNAL(guideModeClicked()), this, SLOT(slotShowGuidePage()));
connect(pPageCart, &PageCart::requestCheckout, this, &MainWidget::onCheckoutRequested);
connect(pPageCart, SIGNAL(goWelcome()), this, SLOT(slotShowWelcomePage()));
connect(pPagePay, SIGNAL(creditCardClicked()), this, SLOT(slotShowPayCardPage()));
connect(pPagePay, SIGNAL(backCartClicked()), this, SLOT(slotShowCartPage()));
connect(pPageCard, SIGNAL(goTotalPayClicked()), this, SLOT(slotShowTotalPayPage_2()));
connect(pPageTotalPay, &PageTotalPay::backToStartClicked, this, &MainWidget::slotShowWelcomePage);
connect(ui->pstackedWidget, &QStackedWidget::currentChanged, this, &MainWidget::onPageChanged);
onPageChanged(ui->pstackedWidget->currentIndex());
}
MainWidget::~MainWidget()
{
delete ui;
}
void MainWidget::sendUdpData(const QString &cmd)
{
QByteArray data = cmd.toUtf8();
m_udpSocket->writeDatagram(data, QHostAddress(ROS_SERVER_IP), ROS_SERVER_PORT);
qDebug() << "[UDP Send]" << cmd << "to" << ROS_SERVER_IP;
}
void MainWidget::onCheckoutRequested(double expectedWeight)
{
if (m_isCheckingWeight) {
qDebug() << "Check skipped: Already verifying weight...";
return;
}
m_isCheckingWeight = true; // Lock 설정
QString urlStr = QString("http://%1:%2/cart/check_weight")
.arg(API_SERVER_IP)
.arg(API_SERVER_PORT);
QUrl url(urlStr);
QNetworkRequest request(url);
// GET 요청 전송 (응답은 onCheckWeightResponse 에서 처리)
m_networkManager->get(request);
qDebug() << "[API Request] Check Weight ->" << urlStr;
}
// ROS에서 온 UDP 수신
void MainWidget::onUdpReadyRead()
{
while (m_udpSocket->hasPendingDatagrams()) {
QNetworkDatagram datagram = m_udpSocket->receiveDatagram();
QString msg = QString::fromUtf8(datagram.data()).trimmed();
qDebug() << "[UDP Recv]" << msg;
if (msg == "STATUS:ARRIVED") {
emit robotArrivedSignal();
}
else if (msg == "STATUS:WEIGHT_OK") {
// [성공] 무게가 일치함 -> 결제 페이지로 이동
proceedToPayPage();
}
else if (msg == "ERROR:WEIGHT_MISMATCH") { // 데이터 파싱 (콜론 : 으로 나눔)
QStringList parts = msg.split(":");
double expected = 0.0;
double real = 0.0;
double diff = 0.0;
// 데이터가 같이 왔는지 확인 (안 왔으면 0.0으로 표시됨)
if (parts.size() >= 5) {
expected = parts[2].toDouble();
real = parts[3].toDouble();
diff = parts[4].toDouble();
}
// 요청하신 팝업 코드 적용
QString alertMsg = QString("상품 무게가 일치하지 않습니다.\n\n"
"예상 무게: %1 g\n"
"실제 무게: %2 g\n"
"차이: %3 g\n\n"
"카트의 물건을 확인해주세요.")
.arg(expected, 0, 'f', 1)
.arg(real, 0, 'f', 1)
.arg(diff, 0, 'f', 1);
QMessageBox::warning(this, "출발 불가", alertMsg);
}
}
}
void MainWidget::proceedToPayPage()
{
auto cartLines = pPageCart->getCartLines();
QVector<PagePay::PayLine> payLines;
payLines.reserve(cartLines.size());
for (const auto &c : cartLines) {
PagePay::PayLine p;
p.name = c.name;
p.qty = c.qty;
p.unitPrice = c.unitPrice;
payLines.push_back(p);
}
pPagePay->setPayItems(payLines);
ui->pstackedWidget->setCurrentWidget(pPagePay);
}
void MainWidget::onGoalRequestReceived(double x, double y)
{
QString cmd = QString("GOAL:%1,%2").arg(x).arg(y);
sendUdpData(cmd);
}
void MainWidget::onStopRequestReceived()
{
sendUdpData("STOP");
}
// 페이지 변경 시 모드 변경
void MainWidget::onPageChanged(int index)
{
QWidget *currentWidget = ui->pstackedWidget->widget(index);
int mode = 0;
if (currentWidget == pPageWelcome) mode = 0; // 정지
else if (currentWidget == pPageCart) mode = 1; // 따라가기
else if (currentWidget == pPageGuide) mode = 2; // 안내모드
else if (currentWidget == pPagePay) mode = 0; // 결제 중 정지
else mode = 0;
sendRobotMode(mode);
}
void MainWidget::sendRobotMode(int mode)
{
sendUdpData(QString("MODE:%1").arg(mode));
}
void MainWidget::on_pPBStartClicked() { ui->pstackedWidget->setCurrentWidget(pPageCart); }
void MainWidget::slotShowCartPage() { ui->pstackedWidget->setCurrentWidget(pPageCart); }
void MainWidget::slotShowGuidePage() { ui->pstackedWidget->setCurrentWidget(pPageGuide); }
void MainWidget::slotShowPayCardPage(){ ui->pstackedWidget->setCurrentWidget(pPageCard); }
void MainWidget::slotShowTotalPayPage_2() { ui->pstackedWidget->setCurrentWidget(pPageTotalPay); }
void MainWidget::slotShowWelcomePage()
{
ui->pstackedWidget->setCurrentWidget(pPageWelcome);
pPageCart->resetCart();
}
void MainWidget::slotShowPayPage()
{
auto cartLines = pPageCart->getCartLines();
QVector<PagePay::PayLine> payLines;
payLines.reserve(cartLines.size());
for (const auto &c : cartLines) {
PagePay::PayLine p;
p.name = c.name;
p.qty = c.qty;
p.unitPrice = c.unitPrice;
payLines.push_back(p);
}
pPagePay->setPayItems(payLines);
ui->pstackedWidget->setCurrentWidget(pPagePay);
}
bool MainWidget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
auto *ke = static_cast<QKeyEvent*>(event);
if (ke->key() == Qt::Key_F12) {
qApp->quit();
return true;
}
}
return QWidget::eventFilter(obj, event);
}
void MainWidget::onCheckWeightResponse(QNetworkReply *reply)
{
// 요청한 URL이 check_weight 인지 확인 (다른 API 요청과 섞이지 않게)
if (!reply->request().url().toString().contains("/cart/check_weight")) {
return;
}
if (reply->error()) {
qDebug() << "API Error:" << reply->errorString();
QMessageBox::critical(this, "통신 오류", "서버와 통신할 수 없습니다.\n" + reply->errorString());
reply->deleteLater();
m_isCheckingWeight = false;
return;
}
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject obj = doc.object();
// Python 서버 응답 필드: expected_weight, real_weight, diff, movable
bool movable = obj["movable"].toBool();
double expected = obj["expected_weight"].toDouble();
double real = obj["real_weight"].toDouble();
double diff = obj["diff"].toDouble();
qDebug() << "[API Response] Movable:" << movable << "Diff:" << diff;
if (movable) {
// [성공] 오차 범위 내 -> 결제 페이지로 이동
proceedToPayPage();
} else {
// [실패] 오차 범위 초과 -> 팝업 띄우기
QString msg = QString("상품 무게가 일치하지 않습니다.\n\n"
"예상 무게: %1 g\n"
"실제 무게: %2 g\n"
"차이: %3 g\n\n"
"카트의 물건을 확인해주세요.")
.arg(expected, 0, 'f', 1)
.arg(real, 0, 'f', 1)
.arg(diff, 0, 'f', 1);
QMessageBox::warning(this, "출발 불가", msg);
}
reply->deleteLater();
m_isCheckingWeight = false;
}
+87
View File
@@ -0,0 +1,87 @@
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include <QtNetwork/QUdpSocket>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QJsonDocument>
#include <QJsonObject>
#define ROS_SERVER_IP "192.168.123.42"
#define ROS_SERVER_PORT 55555
#define API_SERVER_IP "192.168.123.43"
#define API_SERVER_PORT 8000
#define QT_LISTEN_PORT 55555
#include "pagewelcome.h"
#include "pagecart.h"
#include "pageguide.h"
#include "pagepay.h"
#include "pagepay_card.h"
#include "pagetotalpay.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWidget; }
QT_END_NAMESPACE
class MainWidget : public QWidget
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = nullptr);
~MainWidget();
signals:
void robotArrivedSignal();
private:
Ui::MainWidget *ui;
QUdpSocket *m_udpSocket;
QNetworkAccessManager *m_networkManager;
PageWelcome *pPageWelcome;
PageCart *pPageCart;
PageGuide *pPageGuide;
PagePay *pPagePay;
pagepay_card *pPageCard;
PageTotalPay *pPageTotalPay;
bool m_isCheckingWeight;
void sendUdpData(const QString &cmd);
void sendRobotMode(int mode); // 모드 전송 (0:정지, 1:따라가기, 2:안내)
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots:
void onUdpReadyRead(); // UDP 수신부 (무게 검증 결과 처리)
void onCheckWeightResponse(QNetworkReply *reply);
void on_pPBStartClicked();
void slotShowPayPage();
void slotShowCartPage();
void slotShowGuidePage();
void slotShowPayCardPage();
void slotShowTotalPayPage_2();
void slotShowWelcomePage();
void onCheckoutRequested(double expectedWeight);
void proceedToPayPage();
void onGoalRequestReceived(double x, double y);
void onStopRequestReceived();
void onPageChanged(int index);
};
#endif // MAINWIDGET_H
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWidget</class>
<widget class="QWidget" name="MainWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>565</width>
<height>348</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QStackedWidget" name="pstackedWidget">
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_2"/>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+408
View File
@@ -0,0 +1,408 @@
#include "pagecart.h"
#include "ui_pagecart.h"
#include <QGraphicsDropShadowEffect>
#include <QPushButton>
#include <QTableWidgetItem>
#include <QDebug>
#include <QMessageBox>
#include <QKeyEvent>
#include <QApplication>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QLabel>
#include <QPixmap>
#include <cmath>
static QString moneyKR(qint64 v)
{
const QLocale loc(QLocale::Korean, QLocale::SouthKorea);
return loc.toString(v) + "원";
}
static QString imageForName(const QString& name)
{
if(name=="홈런볼 초코맛") return ":/cart_snack(1).jpg";
if(name=="허니버터칩") return ":/cart_snack(2).png";
if(name=="오레오 초콜릿크림") return ":/cart_snack(3).jpg";
if(name=="빼빼로 아몬드") return ":/cart_snack(4).jpg";
if(name=="빈츠") return ":/cart_snack(5).jpg";
if(name=="예감 치즈그르탕") return ":/cart_snack(6).jpg";
if(name=="오!감자 감자그라탕") return ":/cart_snack(7).jpg";
if(name=="포카칩 오리지널") return ":/cart_snack(8).jpg";
if(name=="아이폰 15 프로") return ":/cart_iphone.jpg";
if(name=="핸드크림") return ":/cart_handcream.jpg";
if(name=="크리스마스 퍼즐") return ":/cart_puzzle.png";
return "";
}
PageCart::PageCart(QWidget *parent)
: QWidget(parent),
ui(new Ui::PageCart)
{
ui->setupUi(this);
// 그림자 효과
auto makeShadow = [](QObject *parent){
auto *s = new QGraphicsDropShadowEffect(parent);
s->setBlurRadius(18);
s->setOffset(0, 4);
s->setColor(QColor(0, 0, 0, 60));
return s;
};
ui->tableCart->setStyleSheet(
"QTableWidget::item { padding: 0px; margin: 0px; }"
"QTableWidget { border: none; }"
);
if (ui->widget_2) ui->widget_2->setGraphicsEffect(makeShadow(ui->widget_2));
if (ui->widget) ui->widget->setGraphicsEffect(makeShadow(ui->widget));
ui->tableCart->setSelectionMode(QAbstractItemView::NoSelection);
ui->tableCart->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tableCart->setShowGrid(false);
// 바코드 입력용 숨김 QLineEdit
m_editBarcode = new QLineEdit(this);
m_editBarcode->setVisible(false);
m_editBarcode->setFocusPolicy(Qt::StrongFocus);
m_editBarcode->setFocus();
connect(m_editBarcode, SIGNAL(returnPressed()), this, SLOT(onBarcodeEntered()));
qApp->installEventFilter(this);
m_scanner = new BarcodeScanner(this);
connect(m_scanner, &BarcodeScanner::itemFetched, this, &PageCart::handleItemFetched);
connect(m_scanner, &BarcodeScanner::fetchFailed, this, &PageCart::handleFetchFailed);
// 테이블 컬럼 설정
ui->tableCart->setColumnCount(7);
ui->tableCart->horizontalHeader()->setVisible(false);
ui->tableCart->verticalHeader()->setVisible(false);
ui->tableCart->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
ui->tableCart->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
ui->tableCart->setColumnWidth(0, 54);
ui->tableCart->setColumnWidth(2, 52);
ui->tableCart->setColumnWidth(3, 42);
ui->tableCart->setColumnWidth(5, 76);
ui->tableCart->setColumnWidth(6, 46);
updateTotal();
if (ui->btnHome) {
connect(ui->btnHome, &QPushButton::clicked, this, [this](){
emit goWelcome();
});
}
}
PageCart::~PageCart()
{
delete ui;
}
// MainWidget으로부터 API 설정 주입
void PageCart::setApiConfig(const QString &ip, int port)
{
if (m_scanner) {
m_scanner->setApiBaseUrl(ip, port);
}
resetCart();
}
// 카트 행 추가
void PageCart::addRowForItem(const QString& id, const QString& name, int unitPrice, int qty, double weight)
{
int row = ui->tableCart->rowCount();
ui->tableCart->insertRow(row);
ui->tableCart->setRowHeight(row, 60);
m_unitPrice.append(unitPrice);
ItemInfo info;
info.id = id;
info.name = name;
info.price = unitPrice;
info.weight = weight;
m_items.append(info);
auto makeCenterCell = [&](QWidget *child) -> QWidget* {
QWidget *w = new QWidget(ui->tableCart);
auto *lay = new QHBoxLayout(w);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
lay->setAlignment(Qt::AlignCenter);
lay->addWidget(child);
return w;
};
auto makeRightCell = [&](QWidget *child) -> QWidget* {
QWidget *w = new QWidget(ui->tableCart);
auto *lay = new QHBoxLayout(w);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
lay->addStretch();
lay->addWidget(child);
return w;
};
// 이미지
QLabel *img = new QLabel(ui->tableCart);
img->setFixedSize(44, 44);
img->setAlignment(Qt::AlignCenter);
QPixmap px(imageForName(name));
if (!px.isNull()) {
img->setPixmap(px.scaled(44, 44, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
}
ui->tableCart->setCellWidget(row, 0, makeCenterCell(img));
ui->tableCart->setItem(row, 1, new QTableWidgetItem(name));
QPushButton *btnPlus = new QPushButton("+", ui->tableCart);
btnPlus->setFixedSize(26, 30);
btnPlus->setStyleSheet("QPushButton { background-color: rgb(37,99,235); color: white; border-radius: 10px; font-weight: bold; }");
ui->tableCart->setCellWidget(row, 2, makeCenterCell(btnPlus));
connect(btnPlus, &QPushButton::clicked, this, &PageCart::onPlusClicked);
QTableWidgetItem *qtyItem = new QTableWidgetItem(QString::number(qty));
qtyItem->setTextAlignment(Qt::AlignCenter);
ui->tableCart->setItem(row, 3, qtyItem);
QPushButton *btnMinus = new QPushButton("-", ui->tableCart);
btnMinus->setFixedSize(30, 30);
btnMinus->setStyleSheet("QPushButton { background-color: rgb(154,153,150); color: white; border-radius: 10px; font-weight: bold; }");
ui->tableCart->setCellWidget(row, 4, makeCenterCell(btnMinus));
connect(btnMinus, &QPushButton::clicked, this, &PageCart::onMinusClicked);
QTableWidgetItem *priceItem = new QTableWidgetItem(moneyKR(0));
priceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->tableCart->setItem(row, 5, priceItem);
// 삭제 버튼
QPushButton *btnDelete = new QPushButton("X", ui->tableCart);
btnDelete->setFixedSize(30, 30);
btnDelete->setStyleSheet("QPushButton { background-color: rgb(224,27,36); color: white; border-radius: 10px; font-weight: bold; }");
ui->tableCart->setCellWidget(row, 6, makeRightCell(btnDelete));
connect(btnDelete, &QPushButton::clicked, this, &PageCart::onDeleteClicked);
updateRowAmount(row);
}
void PageCart::onPlusClicked()
{
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = getRowFromButton(btn);
if (row < 0 || row >= m_items.size()) return;
QString itemId = m_items[row].id;
m_scanner->fetchItemDetails(itemId);
}
void PageCart::onMinusClicked()
{
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = getRowFromButton(btn);
if (row < 0 || row >= m_items.size()) return;
QString itemId = m_items[row].id;
m_scanner->removeItem(itemId);
int currentQty = ui->tableCart->item(row, 3)->text().toInt();
int newQty = currentQty - 1;
if (newQty <= 0) {
m_unitPrice.removeAt(row);
m_items.removeAt(row);
ui->tableCart->removeRow(row);
} else {
ui->tableCart->item(row, 3)->setText(QString::number(newQty));
updateRowAmount(row);
}
updateTotal();
}
void PageCart::onDeleteClicked()
{
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = getRowFromButton(btn);
if (row < 0 || row >= m_items.size()) return;
QString itemId = m_items[row].id;
int qty = ui->tableCart->item(row, 3)->text().toInt();
for (int i = 0; i < qty; ++i) {
m_scanner->removeItem(itemId);
}
m_unitPrice.removeAt(row);
m_items.removeAt(row);
ui->tableCart->removeRow(row);
updateTotal();
}
void PageCart::updateRowAmount(int row)
{
if (row < 0 || row >= m_unitPrice.size()) return;
int qty = ui->tableCart->item(row, 3)->text().toInt();
int amount = m_unitPrice[row] * qty;
ui->tableCart->item(row, 5)->setText(moneyKR(amount));
}
void PageCart::updateTotal()
{
int totalCount = 0;
int totalPrice = 0;
for (int r = 0; r < ui->tableCart->rowCount(); ++r) {
auto *qtyItem = ui->tableCart->item(r, 3);
if (!qtyItem) continue;
int qty = qtyItem->text().toInt();
totalCount += qty;
int unit = (r < m_unitPrice.size()) ? m_unitPrice[r] : 0;
totalPrice += unit * qty;
}
if (ui->lblTotalPrice_2)
ui->lblTotalPrice_2->setText(moneyKR(totalPrice));
if (ui->lblCartTitle)
ui->lblCartTitle->setText(QString("Cart (%1 products)").arg(totalCount));
}
void PageCart::onBarcodeEntered()
{
QString code = m_editBarcode->text().trimmed();
m_editBarcode->clear();
if (code.isEmpty()) return;
m_scanner->fetchItemDetails(code);
}
bool PageCart::eventFilter(QObject *obj, QEvent *event)
{
Q_UNUSED(obj);
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
if (!m_barcodeData.isEmpty()) {
m_scanner->fetchItemDetails(m_barcodeData);
m_barcodeData.clear();
}
return true;
}
else if (!keyEvent->text().isEmpty() &&
!(keyEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier))) {
m_barcodeData.append(keyEvent->text());
return true;
}
}
return QWidget::eventFilter(obj, event);
}
void PageCart::handleItemFetched(const Item &item)
{
QString strId = item.id;
int rowFound = -1;
for (int r = 0; r < ui->tableCart->rowCount(); ++r) {
if (m_items[r].id == strId || m_items[r].name == item.name) {
rowFound = r;
break;
}
}
if (rowFound == -1) {
int unit = static_cast<int>(std::lround(item.price));
addRowForItem(strId, item.name, unit, 1, item.weight);
} else {
int qty = ui->tableCart->item(rowFound, 3)->text().toInt();
ui->tableCart->item(rowFound, 3)->setText(QString::number(qty + 1));
m_items[rowFound].id = strId;
m_items[rowFound].weight = item.weight;
updateRowAmount(rowFound);
}
updateTotal();
}
void PageCart::handleFetchFailed(const QString &err)
{
qDebug() << "Barcode fetch failed:" << err;
QMessageBox::warning(this, "스캔 실패", "상품 정보를 불러오지 못했습니다.\n" + err);
}
void PageCart::on_btnGuide_clicked() { emit guideModeClicked(); }
void PageCart::on_pushButton_clicked() { resetCart(); }
QVector<PageCart::CartLine> PageCart::getCartLines() const
{
QVector<CartLine> out;
for (int r = 0; r < ui->tableCart->rowCount(); ++r) {
auto *nameItem = ui->tableCart->item(r, 1);
auto *qtyItem = ui->tableCart->item(r, 3);
if (!nameItem || !qtyItem) continue;
int qty = qtyItem->text().toInt();
if (qty <= 0) continue;
int unitPrice = (r < m_unitPrice.size()) ? m_unitPrice[r] : 0;
out.push_back({ nameItem->text(), qty, unitPrice });
}
return out;
}
void PageCart::resetCart()
{
ui->tableCart->setRowCount(0);
m_unitPrice.clear();
m_items.clear();
updateTotal();
}
int PageCart::getRowFromButton(QWidget *btn)
{
if (!btn) return -1;
int rowCount = ui->tableCart->rowCount();
for (int r = 0; r < rowCount; ++r) {
int targetCols[] = {2, 4, 6};
for (int c : targetCols) {
QWidget *cellWidget = ui->tableCart->cellWidget(r, c);
if (cellWidget && (cellWidget == btn || cellWidget->isAncestorOf(btn))) {
return r;
}
}
}
return -1;
}
double PageCart::getTotalWeight() const
{
double totalWeight = 0.0;
// m_items와 테이블의 행은 동기화되어 있다고 가정
for(int i = 0; i < m_items.size(); ++i) {
if (i >= ui->tableCart->rowCount()) break;
QTableWidgetItem *itemQty = ui->tableCart->item(i, 3);
if (itemQty) {
int qty = itemQty->text().toInt();
totalWeight += (m_items[i].weight * qty);
}
}
return totalWeight;
}
void PageCart::on_btnCheckout_clicked()
{
if (ui->tableCart->rowCount() == 0) {
QMessageBox::information(this, "알림", "장바구니가 비어있습니다.");
return;
}
// [수정] 예상 무게를 계산하여 MainWidget으로 전달
double expected = getTotalWeight();
qDebug() << "Request Checkout - Expected Weight:" << expected;
emit requestCheckout(expected);
}
+84
View File
@@ -0,0 +1,84 @@
#ifndef PAGECART_H
#define PAGECART_H
#include <QWidget>
#include <QVector>
#include <QLineEdit>
#include <QString>
#include "barcodescanner.h"
namespace Ui { class PageCart; }
struct ItemInfo {
QString id;
QString name;
int price;
double weight;
};
class PageCart : public QWidget
{
Q_OBJECT
public:
explicit PageCart(QWidget *parent = nullptr);
~PageCart();
struct CartLine {
QString name;
int qty;
int unitPrice;
};
QVector<CartLine> getCartLines() const;
// MainWidget에서 API 설정 받기
void setApiConfig(const QString &ip, int port);
double getTotalWeight() const;
public slots:
void resetCart();
signals:
void guideModeClicked();
void goWelcome();
void requestCheckout(double totalWeight);
private slots:
void onPlusClicked();
void onMinusClicked();
void onDeleteClicked();
void onBarcodeEntered();
void handleItemFetched(const Item &item);
void handleFetchFailed(const QString &err);
void on_pushButton_clicked(); // 카트 비우기
void on_btnCheckout_clicked(); // 결제 버튼
void on_btnGuide_clicked(); // 안내 버튼
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private:
Ui::PageCart *ui;
QVector<ItemInfo> m_items;
QVector<int> m_unitPrice;
QLineEdit *m_editBarcode = nullptr;
QString m_barcodeData;
BarcodeScanner *m_scanner = nullptr;
void addRowForItem(const QString& id, const QString& name, int unitPrice, int qty, double weight);
void updateRowAmount(int row);
void updateTotal();
int getRowFromButton(QWidget *btn);
};
#endif // PAGECART_H
+478
View File
@@ -0,0 +1,478 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageCart</class>
<widget class="QWidget" name="PageCart">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(242, 244, 245);
</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QWidget" name="widget_2" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>531</width>
<height>371</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-radius: 16px;
</string>
</property>
<widget class="QTableWidget" name="tableCart">
<property name="geometry">
<rect>
<x>10</x>
<y>120</y>
<width>511</width>
<height>241</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_10">
<property name="geometry">
<rect>
<x>50</x>
<y>70</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 14pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Product</string>
</property>
</widget>
<widget class="QLabel" name="label_11">
<property name="geometry">
<rect>
<x>230</x>
<y>70</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 14pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Count</string>
</property>
</widget>
<widget class="QLabel" name="label_12">
<property name="geometry">
<rect>
<x>415</x>
<y>70</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 14pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Price</string>
</property>
</widget>
<widget class="QFrame" name="frame_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>531</width>
<height>61</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-style: solid;
border-top: 1px solid rgb(255, 255, 255);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(200, 200, 200);
border-radius: 0px;
</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 23pt &quot;Umpush&quot;;
border-style: solid;
border-top: 1px solid rgb(255, 255, 255);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
border-radius: 0px;
</string>
</property>
<property name="text">
<string>Cart</string>
</property>
</widget>
<widget class="QLabel" name="lblCartTitle">
<property name="geometry">
<rect>
<x>90</x>
<y>20</y>
<width>151</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Umpush&quot;;
border-style: solid;
border-top: 1px solid rgb(255, 255, 255);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
border-radius: 0px;
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>410</x>
<y>10</y>
<width>111</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: rgb(224, 27, 36);
font: 300 12pt &quot;Umpush&quot;;
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;
}
QPushButton:hover {
background-color: rgb(192, 28, 40);
}
QPushButton:pressed {
background-color: rgb(120, 0, 10);
}
QPushButton:disabled {
background-color: rgb(120, 120, 120);
color: rgb(230, 230, 230);
}
</string>
</property>
<property name="text">
<string>clear cart</string>
</property>
</widget>
</widget>
</widget>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>550</x>
<y>60</y>
<width>191</width>
<height>261</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<widget class="QFrame" name="frame">
<property name="geometry">
<rect>
<x>0</x>
<y>180</y>
<width>351</width>
<height>81</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-style: solid;
border-top: 1px solid rgb(200, 200, 200);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
border-radius: 0px;
</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QPushButton" name="btnCheckout">
<property name="geometry">
<rect>
<x>3</x>
<y>21</y>
<width>181</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(53, 132, 228);
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;
</string>
</property>
<property name="text">
<string>결제하기</string>
</property>
</widget>
</widget>
<widget class="QLabel" name="label_13">
<property name="geometry">
<rect>
<x>0</x>
<y>30</y>
<width>321</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/new/prefix1/Gemini_Generated_Image_u34sf6u34sf6u34s.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>111</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 18pt &quot;Umpush&quot;;
background-color: rgb(255, 255, 255);
border: none;
border-radius: 16px;
</string>
</property>
<property name="text">
<string>총 금액</string>
</property>
</widget>
<widget class="QLabel" name="lblTotalPrice_2">
<property name="geometry">
<rect>
<x>20</x>
<y>100</y>
<width>161</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 21pt &quot;Umpush&quot;;
background-color: rgb(255, 255, 255);
border: none;
border-radius: 16px;
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
<widget class="QPushButton" name="btnGuide">
<property name="geometry">
<rect>
<x>640</x>
<y>10</y>
<width>41</width>
<height>41</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">font: 23pt &quot;Ria Sans&quot;;
image: url(:/etc/search.png);
border-radius: 0px;
background-color: rgb(242, 244, 245);
</string>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>28</width>
<height>80</height>
</size>
</property>
</widget>
<widget class="QPushButton" name="btnHome">
<property name="geometry">
<rect>
<x>700</x>
<y>10</y>
<width>41</width>
<height>41</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">font: 23pt &quot;Ria Sans&quot;;
image: url(:/etc/home.png);
border-radius: 0px;
padding: 0px 0px;
background-color: rgb(242, 244, 245);
</string>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>28</width>
<height>80</height>
</size>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>330</x>
<y>20</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(0, 0, 0);
color: rgb(154, 153, 150);</string>
</property>
<property name="text">
<string>O Checkout </string>
</property>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>470</x>
<y>19</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(154, 153, 150);
background-color: #F2F4F7;</string>
</property>
<property name="text">
<string>O Payment </string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>220</x>
<y>19</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
background-color: #F2F4F7;
background-color: #F2F4F7;</string>
</property>
<property name="text">
<string>O Cart </string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>550</x>
<y>330</y>
<width>191</width>
<height>101</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/etc/counter.png);
border-radius: 16px;
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+180
View File
@@ -0,0 +1,180 @@
#include "pageguide.h"
#include "ui_pageguide.h"
#include <QPixmap>
#include <QDebug>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QResizeEvent>
#include <QDialog>
#include <QLabel>
#include <QGraphicsDropShadowEffect>
PageGuide::PageGuide(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PageGuide)
{
ui->setupUi(this);
QPixmap pm(":/etc/guide_map.png");
if(!pm.isNull()) {
m_treePixmap = pm;
ui->lblmap->setPixmap(pm);
ui->lblmap->setScaledContents(true);
}
ui->lblmap->setStyleSheet("border-radius:14px; background:#ffffff; border: none;");
// 버튼 연결
connect(ui->btnBackToCart, &QPushButton::clicked, this, &PageGuide::onBackToCartClicked);
connect(ui->btnpuzzle, &QPushButton::clicked, this, &PageGuide::onPuzzleClicked);
connect(ui->btncream, &QPushButton::clicked, this, &PageGuide::onCreamClicked);
connect(ui->btnsnack, &QPushButton::clicked, this, &PageGuide::onSnackClicked);
connect(ui->btnpay, &QPushButton::clicked, this, &PageGuide::onPayClicked);
connect(ui->btnphone, &QPushButton::clicked, this, &PageGuide::onbtnphoneClicked);
}
PageGuide::~PageGuide()
{
delete ui;
}
void PageGuide::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
applyTreePixmap();
}
void PageGuide::applyTreePixmap()
{
if (m_treePixmap.isNull() || !ui->lblmap) return;
QSize target = ui->lblmap->size();
QPixmap scaled = m_treePixmap.scaled(target, Qt::KeepAspectRatio, Qt::SmoothTransformation);
ui->lblmap->setPixmap(scaled);
}
void PageGuide::onBackToCartClicked()
{
emit backToCartClicked();
}
void PageGuide::onPuzzleClicked()
{
emit requestGoal(-0.1, -2.4);
showMovePopup("01코너 식품");
}
void PageGuide::onSnackClicked()
{
emit requestGoal(3.0, 0.55);
showMovePopup("02코너 장난감");
}
void PageGuide::onCreamClicked()
{
emit requestGoal(2.6, -6.5);
showMovePopup("03코너 생필품");
}
void PageGuide::onbtnphoneClicked()
{
emit requestGoal(-0.1, -5.0);
showMovePopup("04코너 전자제품");
}
void PageGuide::onPayClicked()
{
emit requestGoal(-0.07, -6.7);
showMovePopup("결제 구역");
}
void PageGuide::showMovePopup(const QString &zoneText)
{
// 이미 떠있는 팝업이 있다면 닫기
if (m_currentPopup) {
m_currentPopup->close();
delete m_currentPopup;
m_currentPopup = nullptr;
}
QWidget *parentW = this->window();
m_currentPopup = new QDialog(parentW);
QDialog &dlg = *m_currentPopup;
dlg.setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
dlg.setModal(true);
dlg.setAttribute(Qt::WA_DeleteOnClose);
dlg.setStyleSheet("QDialog { background: transparent; }");
dlg.setFixedSize(520, 260);
// 팝업이 닫힐 때 포인터 초기화
connect(&dlg, &QDialog::destroyed, this, [this](){
m_currentPopup = nullptr;
});
// 중앙 정렬
const QPoint parentTopLeft = parentW->mapToGlobal(QPoint(0, 0));
const int x = parentTopLeft.x() + (parentW->width() - dlg.width()) / 2;
const int y = parentTopLeft.y() + (parentW->height() - dlg.height()) / 2;
dlg.move(x, y);
// 카드 스타일 UI 구성
QFrame *card = new QFrame(&dlg);
card->setObjectName("popupCard");
card->setStyleSheet("#popupCard { background: rgb(246, 245, 244); border-radius: 16px; }");
auto *shadow = new QGraphicsDropShadowEffect(card);
shadow->setBlurRadius(30);
shadow->setOffset(0, 8);
shadow->setColor(QColor(0, 0, 0, 80));
card->setGraphicsEffect(shadow);
QVBoxLayout *root = new QVBoxLayout(&dlg);
root->setContentsMargins(0, 0, 0, 0);
root->addWidget(card);
QVBoxLayout *lay = new QVBoxLayout(card);
lay->setContentsMargins(28, 24, 28, 24);
lay->setSpacing(14);
QLabel *title = new QLabel(QString("%1로 이동 중입니다").arg(zoneText), card);
title->setAlignment(Qt::AlignCenter);
title->setStyleSheet("font-size:24px; font-weight:900; color:#111827;");
QLabel *desc = new QLabel("확인을 누르면 이동을 중단합니다.", card);
desc->setAlignment(Qt::AlignCenter);
desc->setStyleSheet("font-size:16px; font-weight:700; color:#6B7280;");
QPushButton *stopBtn = new QPushButton("확인(이동 중단)", card);
stopBtn->setFixedHeight(48);
stopBtn->setMinimumWidth(220);
stopBtn->setStyleSheet(
"QPushButton{ background:#EF4444; color:white; border:none; border-radius:12px; "
"font-size:18px; font-weight:900; padding:8px 18px; }"
"QPushButton:hover{ background:#DC2626; }"
"QPushButton:pressed{ background:#B91C1C; }"
);
lay->addStretch();
lay->addWidget(title);
lay->addWidget(desc);
lay->addSpacing(8);
lay->addWidget(stopBtn, 0, Qt::AlignHCenter);
lay->addStretch();
// 안내 중지 버튼 -> MainWidget에 신호
connect(stopBtn, &QPushButton::clicked, this, [this, &dlg](){
emit requestStop();
dlg.accept();
});
dlg.show();
}
// 도착 알림 -> 팝업 끄기
void PageGuide::onRobotArrived()
{
if (m_currentPopup) {
qDebug() << "PageGuide: Robot Arrived! Closing popup.";
m_currentPopup->accept();
}
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef PAGEGUIDE_H
#define PAGEGUIDE_H
#include <QWidget>
#include <QPixmap>
#include <QDialog>
namespace Ui { class PageGuide; }
class PageGuide : public QWidget
{
Q_OBJECT
public:
explicit PageGuide(QWidget *parent = nullptr);
~PageGuide();
public slots:
void onRobotArrived();
signals:
void backToCartClicked();
void requestGoal(double x, double y);
void requestStop();
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
void onBackToCartClicked();
void onPuzzleClicked();
void onCreamClicked();
void onSnackClicked();
void onPayClicked();
void onbtnphoneClicked();
private:
Ui::PageGuide *ui;
QPixmap m_treePixmap;
QDialog *m_currentPopup = nullptr;
void applyTreePixmap();
void showMovePopup(const QString &zoneText);
};
#endif // PAGEGUIDE_H
+438
View File
@@ -0,0 +1,438 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageGuide</class>
<widget class="QWidget" name="PageGuide">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QFrame" name="frame">
<property name="styleSheet">
<string notr="true">/* ===== 카드(프레임) ===== */
QFrame#frameCornerList {
background-color: rgb(255, 255, 255);
border-radius: 16px;
border: 1px solid rgb(229, 231, 235); /* 연회색 */
}
/* 제목 라벨 */
QLabel#lblCornerTitle {
background: transparent;
color: rgb(17, 24, 39); /* 진한 글씨 */
font-size: 20px;
font-weight: 800;
}
/* ===== 리스트 위젯 ===== */
QListWidget#listCorners {
background: transparent;
border: none;
outline: 0;
padding: 6px;
}
/* 스크롤바 깔끔하게 (선택) */
QListWidget#listCorners QScrollBar:vertical {
width: 10px;
background: transparent;
}
QListWidget#listCorners QScrollBar::handle:vertical {
background: rgb(209, 213, 219);
border-radius: 5px;
min-height: 20px;
}
QListWidget#listCorners QScrollBar::add-line:vertical,
QListWidget#listCorners QScrollBar::sub-line:vertical {
height: 0px;
}
/* ===== 아이템(행) 스타일 ===== */
QListWidget#listCorners::item {
background-color: rgb(249, 250, 251); /* 살짝 회색 */
border: 1px solid rgb(229, 231, 235);
border-radius: 12px;
padding: 14px 16px; /* 안쪽 여백 */
margin: 6px 4px; /* 아이템 간 간격 */
color: rgb(17, 24, 39);
font-size: 16px;
font-weight: 700;
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QPushButton" name="btnpay">
<property name="geometry">
<rect>
<x>90</x>
<y>350</y>
<width>61</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_pay.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="btnpuzzle">
<property name="geometry">
<rect>
<x>390</x>
<y>150</y>
<width>61</width>
<height>61</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_toy_2.png);
QPushButton#Region1:hover{
background-color: rgb(222, 221, 218);
};
QPushButton#Region1:pressed{
background-color: rgb(222, 221, 218);
}</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="btnsnack">
<property name="geometry">
<rect>
<x>190</x>
<y>170</y>
<width>61</width>
<height>71</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_snack.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="btnBackToCart">
<property name="geometry">
<rect>
<x>550</x>
<y>10</y>
<width>201</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(53, 132, 228);
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;
font: 14pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>장바구니로 돌아가기</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>121</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 22pt &quot;Ubuntu&quot;;
color: rgb(46, 194, 126);</string>
</property>
<property name="text">
<string>안내 지도</string>
</property>
</widget>
<widget class="QLabel" name="lblmap">
<property name="geometry">
<rect>
<x>20</x>
<y>70</y>
<width>521</width>
<height>341</height>
</rect>
</property>
<property name="baseSize">
<size>
<width>780</width>
<height>800</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/guide_map.png);
border-style: solid;
border-top: 3px solid rgb(200, 200, 200);
border-left: 3px solid rgb(200, 200, 200);
border-right: 3px solid rgb(200, 200, 200);
border-bottom: 3px solid rgb(200, 200, 200);
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>140</x>
<y>20</y>
<width>391</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 300 italic 11pt &quot;Ubuntu&quot;;
color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>**지도의 아이콘을 터치하면 해당 구역으로 안내가 시작됩니다</string>
</property>
</widget>
<widget class="QPushButton" name="btnphone">
<property name="geometry">
<rect>
<x>100</x>
<y>290</y>
<width>51</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_phone_1.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="btncream">
<property name="geometry">
<rect>
<x>390</x>
<y>350</y>
<width>51</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_cream.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>180</x>
<y>180</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>01</string>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>380</x>
<y>140</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>02</string>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>370</x>
<y>350</y>
<width>21</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>03</string>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>80</x>
<y>290</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>04</string>
</property>
</widget>
<widget class="QFrame" name="frameCornerList">
<property name="geometry">
<rect>
<x>560</x>
<y>60</y>
<width>191</width>
<height>361</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="lblCornerTitle">
<property name="geometry">
<rect>
<x>60</x>
<y>0</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">
font: 700 16pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>물품위치</string>
</property>
</widget>
<widget class="QListWidget" name="listCorners">
<property name="geometry">
<rect>
<x>0</x>
<y>30</y>
<width>191</width>
<height>341</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 11pt &quot;Ubuntu&quot;;</string>
</property>
<item>
<property name="text">
<string>01코너 식품</string>
</property>
</item>
<item>
<property name="text">
<string>02코너 장난감</string>
</property>
</item>
<item>
<property name="text">
<string>03코너 생필품</string>
</property>
</item>
<item>
<property name="text">
<string>04코너 전자제품</string>
</property>
</item>
<item>
<property name="text">
<string>05코너 계산대</string>
</property>
</item>
</widget>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>70</x>
<y>360</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>05</string>
</property>
</widget>
<zorder>lblmap</zorder>
<zorder>btnpay</zorder>
<zorder>btnpuzzle</zorder>
<zorder>btnsnack</zorder>
<zorder>btnBackToCart</zorder>
<zorder>label</zorder>
<zorder>label_3</zorder>
<zorder>btnphone</zorder>
<zorder>btncream</zorder>
<zorder>label_4</zorder>
<zorder>label_5</zorder>
<zorder>label_6</zorder>
<zorder>label_7</zorder>
<zorder>frameCornerList</zorder>
<zorder>label_8</zorder>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+179
View File
@@ -0,0 +1,179 @@
#include "pagepay.h"
#include "ui_pagepay.h"
#include <QGraphicsDropShadowEffect>
#include <QHeaderView>
#include <QTableWidgetItem>
#include <QPushButton>
#include <QPixmap>
static QString moneyKR(qint64 v)
{
// ✅ 무조건 "1,234" 형식으로 나오게(환경 영향 최소화)
const QLocale loc(QLocale::Korean, QLocale::SouthKorea);
return loc.toString(v) + "원";
}
PagePay::PagePay(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PagePay)
{
ui->setupUi(this);
auto makeShadow = [](QObject *parent){
auto *s = new QGraphicsDropShadowEffect(parent);
s->setBlurRadius(18);
s->setOffset(0, 4);
s->setColor(QColor(0, 0, 0, 60));
return s;
};
// 그림자
if (ui->frame_2) ui->frame_2->setGraphicsEffect(makeShadow(ui->frame_2));
if (ui->widget) ui->widget->setGraphicsEffect(makeShadow(ui->widget));
// ✅ 버튼 연결 (새 UI 기준)
if (ui->btnBack) connect(ui->btnBack, &QPushButton::clicked, this, &PagePay::onBackPressed);
if (ui->btnNext) connect(ui->btnNext, &QPushButton::clicked, this, &PagePay::onNextPressed);
// 테이블 세팅
setupUiTable();
// 카드 아이콘(임시 이미지)
if (ui->lblCardIcon) {
QPixmap px(":/etc/card.png");
if (!px.isNull()) {
ui->lblCardIcon->setPixmap(px.scaled(180, 180, Qt::KeepAspectRatio, Qt::SmoothTransformation));
ui->lblCardIcon->setAlignment(Qt::AlignCenter);
} else {
ui->lblCardIcon->setText("CARD");
ui->lblCardIcon->setAlignment(Qt::AlignCenter);
}
}
// frame 스타일
if (ui->frame_2) {
ui->frame_2->setStyleSheet("QFrame { background: white; border-radius: 16px; }");
}
// 안내 문구 (너 UI가 lblCarGuide면 그대로, lblCardGuide면 그걸로)
if (ui->lblCarGuide) {
ui->lblCarGuide->setText("카드를 투입구에 넣어주세요");
}
// 만약 실제로 lblCardGuide라면 위 대신 아래를 쓰면 됨:
// if (ui->lblCardGuide) ui->lblCardGuide->setText("카드를 투입구에 넣어주세요");
// 처음엔 비어있으니 기본 표시
refreshTableAndTotal();
}
PagePay::~PagePay()
{
delete ui;
}
void PagePay::setupUiTable()
{
ui->tablePayItems->horizontalHeader()->setVisible(false);
ui->tablePayItems->verticalHeader()->setVisible(false);
ui->tablePayItems->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tablePayItems->setSelectionMode(QAbstractItemView::NoSelection);
ui->tablePayItems->setShowGrid(false);
// ✅ 5컬럼: [물품][간격][갯수][간격][가격]
ui->tablePayItems->setColumnCount(5);
auto *hh = ui->tablePayItems->horizontalHeader();
hh->setSectionResizeMode(0, QHeaderView::Stretch); // 물품은 남는 폭 다 먹기
hh->setSectionResizeMode(1, QHeaderView::Fixed); // 간격
hh->setSectionResizeMode(2, QHeaderView::Fixed); // 갯수
hh->setSectionResizeMode(3, QHeaderView::Fixed); // 간격
hh->setSectionResizeMode(4, QHeaderView::Fixed); // 가격
// ✅ 800x480 최적값(기본)
int gapW = 12; // 물품-갯수 / 갯수-가격 사이 간격
int qtyW = 60; // "3개" 정도 충분
int priceW = 110; // "13000원" 정도 충분
// ✅ 테이블 폭에 따라 살짝 자동 보정 (작아지면 더 줄임)
int tableW = ui->tablePayItems->viewport()->width();
if (tableW > 0) {
// 물품 칼럼 최소폭을 남기기 위해, 너무 좁으면 qty/price/gap를 줄여줌
int fixed = gapW*2 + qtyW + priceW;
int nameMin = 160; // 물품명 최소로 확보(원하면 140~200 조절)
if (tableW - fixed < nameMin) {
// 줄일 여지가 있는 것들부터 줄임
gapW = 8;
qtyW = 52;
priceW = 78;
}
}
ui->tablePayItems->setColumnWidth(1, gapW);
ui->tablePayItems->setColumnWidth(2, qtyW);
ui->tablePayItems->setColumnWidth(3, gapW);
ui->tablePayItems->setColumnWidth(4, priceW);
// (옵션) 글자와 셀 padding 조금 줄여서 7인치에 더 꽉 차게
ui->tablePayItems->setStyleSheet(
"QTableWidget::item { padding-left: 4px; padding-right: 4px; }"
);
}
void PagePay::setPayItems(const QVector<PayLine>& lines)
{
m_lines = lines;
refreshTableAndTotal();
}
void PagePay::refreshTableAndTotal()
{
ui->tablePayItems->setRowCount(0);
int totalPrice = 0;
for (int i = 0; i < m_lines.size(); ++i) {
const auto &it = m_lines[i];
int row = ui->tablePayItems->rowCount();
ui->tablePayItems->insertRow(row);
ui->tablePayItems->setRowHeight(row, 40); // ✅ 7인치면 40~44 추천
// (0) 물품
auto *nameItem = new QTableWidgetItem(it.name);
nameItem->setTextAlignment(Qt::AlignVCenter | Qt::AlignLeft);
ui->tablePayItems->setItem(row, 0, nameItem);
// (2) 갯수
auto *qtyItem = new QTableWidgetItem(QString("%1개").arg(it.qty));
qtyItem->setTextAlignment(Qt::AlignCenter);
ui->tablePayItems->setItem(row, 2, qtyItem);
// (4) 가격
int linePrice = it.unitPrice * it.qty;
totalPrice += linePrice;
auto *priceItem = new QTableWidgetItem(moneyKR(linePrice));
priceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->tablePayItems->setItem(row, 4, priceItem);
}
if (ui->lblTotalPriceValue)
ui->lblTotalPriceValue->setText(moneyKR(totalPrice));
ui->btnNext->setEnabled(!m_lines.isEmpty());
}
void PagePay::onBackPressed()
{
emit backCartClicked(); // ✅ PageCart로
}
void PagePay::onNextPressed()
{
emit creditCardClicked(); // ✅ pagepay_card로
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef PAGEPAY_H
#define PAGEPAY_H
#include <QWidget>
#include <QVector>
#include <QString>
namespace Ui { class PagePay; }
class PagePay : public QWidget
{
Q_OBJECT
public:
explicit PagePay(QWidget *parent = nullptr);
~PagePay();
struct PayLine {
QString name;
int qty = 0;
int unitPrice = 0;
};
void setPayItems(const QVector<PayLine>& lines);
signals:
void backCartClicked(); // <- 이전 (PageCart로)
void creditCardClicked(); // 다음 (pagepay_card로)
private slots:
void onBackPressed();
void onNextPressed();
private:
void setupUiTable();
void refreshTableAndTotal();
private:
Ui::PagePay *ui;
QVector<PayLine> m_lines;
};
#endif // PAGEPAY_H
+358
View File
@@ -0,0 +1,358 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PagePay</class>
<widget class="QWidget" name="PagePay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: #F2F4F7;</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QPushButton" name="btnNext">
<property name="geometry">
<rect>
<x>460</x>
<y>370</y>
<width>281</width>
<height>61</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(53, 132, 228);
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;
font: 14pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>다음으로 넘어가기</string>
</property>
</widget>
<widget class="QPushButton" name="btnBack">
<property name="geometry">
<rect>
<x>610</x>
<y>10</y>
<width>95</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(154, 153, 150);
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;</string>
</property>
<property name="text">
<string>이전 화면</string>
</property>
</widget>
<widget class="QFrame" name="frame_2">
<property name="geometry">
<rect>
<x>460</x>
<y>90</y>
<width>281</width>
<height>271</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-radius: 16px;</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="lblCardIcon">
<property name="geometry">
<rect>
<x>30</x>
<y>-10</y>
<width>201</width>
<height>211</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/etc/card.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="lblCarGuide">
<property name="geometry">
<rect>
<x>50</x>
<y>200</y>
<width>241</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 500 12pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>카드를 투입구에 넣어주세요</string>
</property>
</widget>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>330</x>
<y>19</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(0, 0, 0);
background-color: #F2F4F7;
</string>
</property>
<property name="text">
<string>O Checkout </string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>220</x>
<y>20</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(154, 153, 150);
background-color: #F2F4F7;</string>
</property>
<property name="text">
<string>O Cart </string>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>301</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(0, 0, 0)</string>
</property>
<property name="text">
<string>아래 구매 내역을 확인해주세요</string>
</property>
</widget>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>441</width>
<height>341</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-radius: 16px;</string>
</property>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>141</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 500 20pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>구매 내역</string>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>91</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 15pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Prdoucts</string>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>230</x>
<y>50</y>
<width>91</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 15pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Count</string>
</property>
</widget>
<widget class="QLabel" name="label_12">
<property name="geometry">
<rect>
<x>385</x>
<y>50</y>
<width>91</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 15pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Price</string>
</property>
</widget>
<widget class="QTableWidget" name="tablePayItems">
<property name="geometry">
<rect>
<x>0</x>
<y>90</y>
<width>441</width>
<height>191</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-radius: 0px;
font: 300 13pt &quot;Umpush&quot;;
border-top: 1px solid #cfcfcf;
border-bottom: 0px;
border-left: 0px;
border-right: 0px;</string>
</property>
</widget>
<widget class="QFrame" name="frame_3">
<property name="geometry">
<rect>
<x>0</x>
<y>290</y>
<width>451</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-style: solid;
border-top: 1px solid rgb(200, 200, 200);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
border-radius: 0px;</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="label_10">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>121</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 16pt &quot;Umpush&quot;;
background-color: rgb(255, 255, 255);
border: none;
</string>
</property>
<property name="text">
<string>총 결제금액</string>
</property>
</widget>
<widget class="QLabel" name="lblTotalPriceValue">
<property name="geometry">
<rect>
<x>240</x>
<y>10</y>
<width>101</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border: none;
font: 15pt &quot;Umpush&quot;;
color: rgb(28, 113, 216);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>460</x>
<y>20</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(154, 153, 150);
background-color: #F2F4F7;</string>
</property>
<property name="text">
<string>O Payment </string>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+213
View File
@@ -0,0 +1,213 @@
#include "pagepay_card.h"
#include "ui_pagepay_card.h"
#include <QDialog>
#include <QFrame>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QStackedWidget>
#include <QTimer>
#include <QShowEvent>
#include <QPainter>
#include <QFont>
#include <QSharedPointer>
// ⏳ 텍스트를 Pixmap으로 렌더링
static QPixmap textToPixmap(const QString &text, int fontPx)
{
QFont f;
f.setPixelSize(fontPx);
const int size = fontPx * 2;
QPixmap pm(size, size);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::TextAntialiasing, true);
p.setFont(f);
p.setPen(Qt::black);
p.drawText(pm.rect(), Qt::AlignCenter, text);
return pm;
}
// Pixmap을 고정 캔버스 안에서 회전
static QPixmap rotatedPixmap(const QPixmap &src, qreal angleDeg)
{
QPixmap out(src.size());
out.fill(Qt::transparent);
QPainter p(&out);
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
const QPointF c(out.width() / 2.0, out.height() / 2.0);
p.translate(c);
p.rotate(angleDeg);
p.translate(-c);
p.drawPixmap(0, 0, src);
return out;
}
pagepay_card::pagepay_card(QWidget *parent)
: QWidget(parent)
, ui(new Ui::pagepay_card)
{
ui->setupUi(this);
}
pagepay_card::~pagepay_card()
{
delete ui;
}
void pagepay_card::showEvent(QShowEvent *e)
{
QWidget::showEvent(e);
// 페이지가 뜬 뒤 모달 띄우기
QTimer::singleShot(0, this, [this]() {
showPaymentModal();
});
}
void pagepay_card::showPaymentModal()
{
QWidget *parentW = this->window();
if (!parentW) parentW = this;
// ✅ 팝업 자체를 카드 크기로만 만들기 (전체 덮지 않음)
QDialog *dlg = new QDialog(parentW);
dlg->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
dlg->setModal(true);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setFixedSize(520, 260);
// ✅ 부모(800x480) 기준 "정중앙" 위치
const int x = (parentW->width() - dlg->width()) / 2;
const int y = (parentW->height() - dlg->height()) / 2;
dlg->move(x, y);
// ===== 다이얼로그 루트 레이아웃 =====
auto *dlgLay = new QVBoxLayout(dlg);
dlgLay->setContentsMargins(0,0,0,0);
dlgLay->setSpacing(0);
// ===== 카드 프레임 (둥근 모서리) =====
QFrame *card = new QFrame(dlg);
card->setObjectName("card");
card->setStyleSheet(
"#card { background: white; border-radius: 16px; }"
);
dlgLay->addWidget(card);
// ===== 카드 내부 스택 =====
QStackedWidget *stack = new QStackedWidget(card);
QVBoxLayout *cardLay = new QVBoxLayout(card);
cardLay->setContentsMargins(24,24,24,24);
cardLay->addWidget(stack);
// --- processing ---
QWidget *wProc = new QWidget(card);
QVBoxLayout *pLay = new QVBoxLayout(wProc);
pLay->setContentsMargins(0,0,0,0);
pLay->setSpacing(12);
QLabel *spinner = new QLabel(wProc);
spinner->setAlignment(Qt::AlignCenter);
spinner->setStyleSheet("background: transparent;");
const QPixmap baseHourglass = textToPixmap("⏳", 52);
spinner->setPixmap(baseHourglass);
QLabel *title = new QLabel("카드를 뽑지말아주세요", wProc);
title->setAlignment(Qt::AlignCenter);
title->setStyleSheet("font-size: 22px; font-weight: 800; color: #111827;");
QLabel *desc = new QLabel("결제가 진행중입니다", wProc);
desc->setAlignment(Qt::AlignCenter);
desc->setStyleSheet("font-size: 14px; color: #6B7280;");
QLabel *countLbl = new QLabel("5", wProc);
countLbl->setAlignment(Qt::AlignCenter);
countLbl->setStyleSheet("font-size: 64px; font-weight: 900; color: #2563EB; margin-top: 6px;");
pLay->addStretch();
pLay->addWidget(spinner);
pLay->addWidget(title);
pLay->addWidget(desc);
pLay->addWidget(countLbl);
pLay->addStretch();
// --- complete ---
QWidget *wDone = new QWidget(card);
QVBoxLayout *dLay = new QVBoxLayout(wDone);
dLay->setContentsMargins(0,0,0,0);
dLay->setSpacing(16);
QLabel *check = new QLabel("✓", wDone);
check->setAlignment(Qt::AlignCenter);
check->setStyleSheet("font-size: 72px; font-weight: 900; color: #22C55E;");
QLabel *doneTxt = new QLabel("결제가 완료되었습니다", wDone);
doneTxt->setAlignment(Qt::AlignCenter);
doneTxt->setStyleSheet("font-size: 22px; font-weight: 800; color: #111827;");
QPushButton *btnOk = new QPushButton("확인", wDone);
btnOk->setFixedHeight(44);
btnOk->setFixedWidth(180);
btnOk->setStyleSheet(
"QPushButton{background:#2563EB;color:white;border:none;border-radius:12px;font-size:16px;font-weight:800; padding: 0 18px;}"
"QPushButton:hover{background:#1D4ED8;}"
"QPushButton:pressed{background:#1E40AF;}"
);
dLay->addStretch();
dLay->addWidget(check);
dLay->addWidget(doneTxt);
dLay->addWidget(btnOk, 0, Qt::AlignHCenter);
dLay->addStretch();
stack->addWidget(wProc);
stack->addWidget(wDone);
stack->setCurrentWidget(wProc);
// ===== (A) ⏳ 회전 타이머 (느리게) =====
auto angle = QSharedPointer<qreal>::create(0.0);
QTimer *spinTimer = new QTimer(dlg);
spinTimer->setInterval(30); // ✅ 16ms(빠름) -> 30ms(느리게)
QObject::connect(spinTimer, &QTimer::timeout, dlg, [=]() mutable {
*angle += 2.0; // ✅ 4.0(빠름) -> 2.0(느리게)
if (*angle >= 360.0) *angle -= 360.0;
spinner->setPixmap(rotatedPixmap(baseHourglass, *angle));
});
spinTimer->start();
// ===== (B) 5초 카운트다운 =====
auto count = QSharedPointer<int>::create(5);
QTimer *t = new QTimer(dlg);
t->setInterval(1000);
QObject::connect(t, &QTimer::timeout, dlg, [=]() mutable {
(*count)--;
if (*count >= 0) countLbl->setText(QString::number(*count));
if (*count <= 0) {
t->stop();
spinTimer->stop();
stack->setCurrentWidget(wDone);
}
});
QObject::connect(btnOk, &QPushButton::clicked, dlg, [this, dlg]() {
dlg->accept();
emit goTotalPayClicked();
});
t->start();
dlg->exec();
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef PAGEPAY_CARD_H
#define PAGEPAY_CARD_H
#include <QWidget>
namespace Ui {
class pagepay_card;
}
class QShowEvent;
class pagepay_card : public QWidget
{
Q_OBJECT
public:
explicit pagepay_card(QWidget *parent = nullptr);
~pagepay_card();
signals:
void goTotalPayClicked();
protected:
void showEvent(QShowEvent *e) override;
private:
void showPaymentModal();
private:
Ui::pagepay_card *ui;
};
#endif // PAGEPAY_CARD_H
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>pagepay_card</class>
<widget class="QWidget" name="pagepay_card">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(36, 31, 49);</string>
</property>
</widget>
<resources/>
<connections/>
</ui>
+14
View File
@@ -0,0 +1,14 @@
#include "pagepayment.h"
#include "ui_pagepayment.h"
pagepayment::pagepayment(QWidget *parent)
: QWidget(parent)
, ui(new Ui::pagepayment)
{
ui->setupUi(this);
}
pagepayment::~pagepayment()
{
delete ui;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef PAGEPAYMENT_H
#define PAGEPAYMENT_H
#include <QWidget>
namespace Ui {
class pagepayment;
}
class pagepayment : public QWidget
{
Q_OBJECT
public:
explicit pagepayment(QWidget *parent = nullptr);
~pagepayment();
private:
Ui::pagepayment *ui;
};
#endif // PAGEPAYMENT_H
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PagePayment</class>
<widget class="QWidget" name="PagePayment">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>230</x>
<y>140</y>
<width>160</width>
<height>80</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</widget>
<resources/>
<connections/>
</ui>
+211
View File
@@ -0,0 +1,211 @@
#include "pagetotalpay.h"
#include "ui_pagetotalpay.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsProxyWidget>
#include <QVariantAnimation>
#include <QVBoxLayout>
#include <QLabel>
#include <QEvent>
#include <QMouseEvent>
#include <QResizeEvent>
#include <QTimer>
#include <QtMath>
#include <QPainter>
#include <QPen>
#include <QEasingCurve>
PageTotalPay::PageTotalPay(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PageTotalPay)
{
ui->setupUi(this);
if (objectName().isEmpty())
setObjectName("PageTotalPay");
// ✅ 여기 추가
setStyleSheet("QWidget#PageTotalPay { background-color: rgb(34,197,94); }");
// ✅ Scene/View
m_scene = new QGraphicsScene(this);
m_view = new QGraphicsView(this);
m_view->setScene(m_scene);
m_view->setFrameShape(QFrame::NoFrame);
m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setStyleSheet("background: transparent;");
m_view->setAlignment(Qt::AlignCenter);
// 클릭 이벤트를 view/viewport에서도 잡기
m_view->viewport()->installEventFilter(this);
m_view->installEventFilter(this);
// ✅ content 위젯(아이콘+텍스트)
m_content = new QWidget();
m_content->setAttribute(Qt::WA_TranslucentBackground);
m_content->installEventFilter(this);
auto *lay = new QVBoxLayout(m_content);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(28);
lay->setAlignment(Qt::AlignCenter);
QLabel *lblIcon = new QLabel(m_content);
lblIcon->setFixedSize(140, 140);
lblIcon->setAlignment(Qt::AlignCenter);
lblIcon->setPixmap(makeCheckIcon(128));
lblIcon->setStyleSheet("background: transparent;");
QLabel *lblTitle = new QLabel("구매해주셔서 감사합니다", m_content);
lblTitle->setAlignment(Qt::AlignCenter);
lblTitle->setStyleSheet(
"background: transparent;"
"color: rgb(0,0,0);"
"font-weight: 900;"
"font-size: 44px;"
);
QLabel *lblSub = new QLabel("화면을 다시 눌러 시작하기", m_content);
lblSub->setAlignment(Qt::AlignCenter);
lblSub->setStyleSheet(
"background: transparent;"
"color: rgb(0,0,0);"
"font-weight: 700;"
"font-size: 22px;"
);
lay->addWidget(lblIcon, 0, Qt::AlignCenter);
lay->addWidget(lblTitle, 0, Qt::AlignCenter);
lay->addWidget(lblSub, 0, Qt::AlignCenter);
// ✅ proxy로 올리기(스케일 애니메이션 대상)
m_proxy = m_scene->addWidget(m_content);
m_proxy->setTransformOriginPoint(m_proxy->boundingRect().center());
// ✅ 레이아웃 붙이기 (중요: 기존 레이아웃 있으면 거기에 붙임)
if (this->layout()) {
this->layout()->setContentsMargins(0,0,0,0);
this->layout()->addWidget(m_view);
} else {
auto *root = new QVBoxLayout(this);
root->setContentsMargins(0,0,0,0);
root->addWidget(m_view);
}
// ✅❗️여기가 너 코드에서 크래시 난 부분: m_pulseAnim을 반드시 생성해야 함
m_pulseAnim = new QVariantAnimation(this);
// ✅ 코사인 루프 (자연스럽게 1.0 -> 1.15 -> 1.0, 점프 없음)
m_pulseAnim->setStartValue(0.0);
m_pulseAnim->setEndValue(2.0 * M_PI);
m_pulseAnim->setDuration(2000);
m_pulseAnim->setLoopCount(-1);
m_pulseAnim->setEasingCurve(QEasingCurve::Linear);
connect(m_pulseAnim, &QVariantAnimation::valueChanged, this, [this](const QVariant &v){
const qreal t = v.toReal();
const qreal amp = 0.15;
const qreal scale = 1.0 + amp * 0.5 * (1.0 - qCos(t));
if (m_proxy) m_proxy->setScale(scale);
});
m_pulseAnim->start();
// ✅ viewport size가 0일 수 있으니 show 이후에 중앙 정렬
QTimer::singleShot(0, this, [this](){ centerProxy(); });
}
PageTotalPay::~PageTotalPay()
{
delete ui;
}
QPixmap PageTotalPay::makeCheckIcon(int sizePx) const
{
const int pad = 6;
QPixmap pm(sizePx + pad*2, sizePx + pad*2);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing, true);
// ✅ 초록색 (Tailwind green-500 비슷)
QColor green(34, 197, 94);
// circle stroke
QPen pen(green); // ✅ 여기!
pen.setWidthF(sizePx * 0.07);
pen.setCapStyle(Qt::RoundCap);
pen.setJoinStyle(Qt::RoundJoin);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
QRectF rc(pad, pad, sizePx, sizePx);
p.drawEllipse(rc);
// check
p.setPen(Qt::NoPen);
p.setBrush(green);
p.drawEllipse(rc);
// 체크: 흰색
QPen pen2(Qt::white);
pen2.setWidthF(sizePx * 0.09);
pen2.setCapStyle(Qt::RoundCap);
pen2.setJoinStyle(Qt::RoundJoin);
p.setPen(pen2);
p.setBrush(Qt::NoBrush);
QPointF a(rc.left() + sizePx*0.28, rc.top() + sizePx*0.55);
QPointF b(rc.left() + sizePx*0.46, rc.top() + sizePx*0.70);
QPointF c(rc.left() + sizePx*0.74, rc.top() + sizePx*0.36);
p.drawLine(a, b);
p.drawLine(b, c);
return pm;
}
void PageTotalPay::centerProxy()
{
if (!m_view || !m_scene || !m_proxy) return;
const QSize vsz = m_view->viewport()->size();
if (vsz.width() <= 0 || vsz.height() <= 0) return; // ✅ 0이면 아직 표시 전
m_scene->setSceneRect(0, 0, vsz.width(), vsz.height());
const QPointF origin = m_proxy->boundingRect().center();
m_proxy->setTransformOriginPoint(origin);
const QPointF sceneCenter = m_scene->sceneRect().center();
m_proxy->setPos(sceneCenter - origin);
}
void PageTotalPay::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
centerProxy();
}
bool PageTotalPay::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress) {
emit backToStartClicked();
return true;
}
return QWidget::eventFilter(obj, event);
}
void PageTotalPay::mousePressEvent(QMouseEvent *e)
{
emit backToStartClicked();
QWidget::mousePressEvent(e);
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef PAGETOTALPAY_H
#define PAGETOTALPAY_H
#include <QWidget>
#include <QPixmap>
class QGraphicsView;
class QGraphicsScene;
class QGraphicsProxyWidget;
class QVariantAnimation;
namespace Ui {
class PageTotalPay;
}
class PageTotalPay : public QWidget
{
Q_OBJECT
public:
explicit PageTotalPay(QWidget *parent = nullptr);
~PageTotalPay();
signals:
void backToStartClicked(); // ✅ 어디든 클릭하면 Welcome으로 돌아가기
protected:
void resizeEvent(QResizeEvent *e) override;
bool eventFilter(QObject *obj, QEvent *event) override;
void mousePressEvent(QMouseEvent *e) override;
private:
QPixmap makeCheckIcon(int sizePx) const;
void centerProxy();
private:
Ui::PageTotalPay *ui = nullptr;
QGraphicsView *m_view = nullptr;
QGraphicsScene *m_scene = nullptr;
QGraphicsProxyWidget *m_proxy = nullptr;
QWidget *m_content = nullptr;
QVariantAnimation *m_pulseAnim = nullptr;
};
#endif // PAGETOTALPAY_H
+73
View File
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageTotalPay</class>
<widget class="QWidget" name="PageTotalPay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(51, 209, 122);</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="lblTitle">
<property name="geometry">
<rect>
<x>260</x>
<y>200</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="lblSubTitle">
<property name="geometry">
<rect>
<x>270</x>
<y>230</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="lblIcon">
<property name="geometry">
<rect>
<x>250</x>
<y>140</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+223
View File
@@ -0,0 +1,223 @@
#include "pagewelcome.h"
#include "ui_pagewelcome.h"
#include <QPushButton>
#include <QPropertyAnimation>
#include <QEasingCurve>
#include <QTimer>
#include <QPixmap>
#include <QDebug>
#include <QShowEvent>
#include <QHideEvent>
#include <QResizeEvent>
PageWelcome::PageWelcome(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PageWelcome)
{
ui->setupUi(this);
// ✅ 버튼 연결
connect(ui->start_button, &QPushButton::clicked,
this, &PageWelcome::onStartClicked);
// ✅ 이미지 로드/세팅(한 번만)
loadPixmapsOnce();
// ✅ UI 레이아웃 적용된 다음 기본 Y 저장 + 중앙 정렬
QTimer::singleShot(0, this, [this]() {
if (ui->start_image && m_baseYStartImage < 0)
m_baseYStartImage = ui->start_image->y();
if (ui->qtcart_text && m_baseYTitle < 0)
m_baseYTitle = ui->qtcart_text->y();
if (ui->start_text && m_baseYStartText < 0)
m_baseYStartText = ui->start_text->y();
if (ui->start_button && m_baseYStartBtn < 0)
m_baseYStartBtn = ui->start_button->y();
resetWelcome();
});
}
PageWelcome::~PageWelcome()
{
if (m_anim) {
m_anim->stop();
delete m_anim;
m_anim = nullptr;
}
delete ui;
}
void PageWelcome::loadPixmapsOnce()
{
if (m_pixLoaded) return;
m_pixLoaded = true;
QPixmap cartPx(":/etc/cart.png");
QPixmap windPx(":/etc/wind.png");
if (ui->cart_image) {
ui->cart_image->setPixmap(cartPx);
ui->cart_image->setScaledContents(true);
ui->cart_image->setAttribute(Qt::WA_TranslucentBackground);
ui->cart_image->show();
}
if (ui->wind_image) {
ui->wind_image->setPixmap(windPx);
ui->wind_image->setScaledContents(true);
ui->wind_image->setAttribute(Qt::WA_TranslucentBackground);
ui->wind_image->show();
}
if (ui->start_image) ui->start_image->show();
}
void PageWelcome::showEvent(QShowEvent *e)
{
QWidget::showEvent(e);
QTimer::singleShot(0, this, [this]() {
resetWelcome();
});
}
void PageWelcome::hideEvent(QHideEvent *e)
{
QWidget::hideEvent(e);
// ✅ 페이지가 숨겨질 때 애니메이션 남아있으면 정리(안전)
if (m_anim) {
m_anim->stop();
m_anim->deleteLater();
m_anim = nullptr;
}
}
void PageWelcome::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
// ✅ 떠나지 않은 상태면 리사이즈 때도 가운데 유지
if (!m_leaving) {
centerStartImage(); // ✅ 기존 유지
centerWelcomeWidgets(); // ✅ 추가
}
}
void PageWelcome::resetWelcome()
{
// ✅ 상태 초기화
m_leaving = false;
// ✅ 버튼 다시 누르게
if (ui->start_button) {
ui->start_button->setEnabled(true);
}
// ✅ 애니메이션 남아있으면 정리
if (m_anim) {
m_anim->stop();
m_anim->deleteLater();
m_anim = nullptr;
}
// ✅ 위젯 다시 보이기
if (ui->start_image) ui->start_image->show();
if (ui->cart_image) ui->cart_image->show();
if (ui->wind_image) ui->wind_image->show();
// 레이아웃 반영 후 이동
QTimer::singleShot(0, this, [this]() {
if (ui->start_image && m_baseYStartImage < 0) m_baseYStartImage = ui->start_image->y();
if (ui->qtcart_text && m_baseYTitle < 0) m_baseYTitle = ui->qtcart_text->y();
if (ui->start_text && m_baseYStartText < 0) m_baseYStartText = ui->start_text->y();
if (ui->start_button&& m_baseYStartBtn < 0) m_baseYStartBtn = ui->start_button->y();
centerStartImage(); // ✅ 기존 유지
centerWelcomeWidgets(); // ✅ 추가
});
}
// ✅ 기존 함수는 그대로: start_image만 가운데
void PageWelcome::centerStartImage()
{
if (!ui->start_image) return;
QWidget *parentW = ui->start_image->parentWidget();
if (!parentW) parentW = this;
const int pw = parentW->width();
const int w = ui->start_image->width();
const int x = (pw - w) / 2;
int y = (m_baseYStartImage >= 0) ? m_baseYStartImage : ui->start_image->y();
ui->start_image->move(x, y);
}
// ✅ 추가: qcart_text, start_text, start_button도 “X만” 중앙정렬
void PageWelcome::centerWelcomeWidgets()
{
QWidget *parentW = this; // 보통 PageWelcome 전체 기준이 가장 깔끔함
const int pw = parentW->width();
auto centerXKeepY = [&](QWidget *w, int baseY){
if (!w) return;
const int x = (pw - w->width()) / 2;
const int y = (baseY >= 0) ? baseY : w->y();
w->move(x, y);
};
centerXKeepY(ui->qtcart_text, m_baseYTitle);
centerXKeepY(ui->start_text, m_baseYStartText);
centerXKeepY(ui->start_button, m_baseYStartBtn);
}
void PageWelcome::onStartClicked()
{
if (m_leaving) return;
m_leaving = true;
if (ui->start_button)
ui->start_button->setEnabled(false);
if (!ui->start_image) {
emit startRequested();
emit startClicked();
return;
}
QWidget *parentW = ui->start_image->parentWidget();
if (!parentW) parentW = this;
const QPoint startPos = ui->start_image->pos();
const int y = startPos.y();
// ✅ 오른쪽 화면 밖으로 슈웅~
const int endX = parentW->width() + ui->start_image->width() + 20;
const QPoint endPos(endX, y);
// ✅ 멤버로 애니메이션 보관(돌아올 때 stop/정리 가능)
m_anim = new QPropertyAnimation(ui->start_image, "pos", this);
m_anim->setDuration(700);
m_anim->setEasingCurve(QEasingCurve::InOutCubic);
m_anim->setStartValue(startPos);
m_anim->setEndValue(endPos);
connect(m_anim, &QPropertyAnimation::finished, this, [this]() {
if (m_anim) {
m_anim->deleteLater();
m_anim = nullptr;
}
emit startRequested();
emit startClicked();
});
m_anim->start();
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef PAGEWELCOME_H
#define PAGEWELCOME_H
#include <QWidget>
class QPropertyAnimation;
class QShowEvent;
class QHideEvent;
class QResizeEvent;
namespace Ui {
class PageWelcome;
}
class PageWelcome : public QWidget
{
Q_OBJECT
public:
explicit PageWelcome(QWidget *parent = nullptr);
~PageWelcome();
signals:
void startClicked();
void startRequested();
protected:
void showEvent(QShowEvent *e) override;
void hideEvent(QHideEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private slots:
void onStartClicked();
private:
void loadPixmapsOnce();
void centerStartImage(); // ✅ 기존: start_image만 가운데
void centerWelcomeWidgets(); // ✅ 추가: 텍스트/버튼도 가운데
void resetWelcome();
private:
Ui::PageWelcome *ui;
bool m_leaving = false;
bool m_pixLoaded = false;
// ✅ 각 위젯의 "기본 Y"를 기억해두고 X만 중앙정렬
int m_baseYStartImage = -1;
int m_baseYTitle = -1; // qcart_text
int m_baseYStartText = -1; // start_text
int m_baseYStartBtn = -1; // start_button
QPropertyAnimation *m_anim = nullptr;
};
#endif // PAGEWELCOME_H
+163
View File
@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageWelcome</class>
<widget class="QWidget" name="PageWelcome">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>11</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="start_text">
<property name="geometry">
<rect>
<x>0</x>
<y>340</y>
<width>781</width>
<height>22</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
</string>
</property>
<property name="text">
<string>시작하기 버튼을 눌러 쇼핑을 시작하세요</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QWidget" name="start_image" native="true">
<property name="geometry">
<rect>
<x>300</x>
<y>80</y>
<width>211</width>
<height>171</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<widget class="QLabel" name="cart_image">
<property name="geometry">
<rect>
<x>90</x>
<y>30</y>
<width>121</width>
<height>101</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="wind_image">
<property name="geometry">
<rect>
<x>0</x>
<y>70</y>
<width>61</width>
<height>71</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
<widget class="QLabel" name="qtcart_text">
<property name="geometry">
<rect>
<x>5</x>
<y>250</y>
<width>771</width>
<height>77</height>
</rect>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="styleSheet">
<string notr="true">font: 700 40pt &quot;Ria Sans&quot;;
color: rgb(87, 227, 137);
background-color: rgb(255, 255, 255);
</string>
</property>
<property name="text">
<string>QT CART</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QPushButton" name="start_button">
<property name="geometry">
<rect>
<x>260</x>
<y>390</y>
<width>261</width>
<height>30</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 11pt &quot;Ria Sans&quot;;
QPushButton {
background-color: rgb(224, 27, 36); /* ✅ 기본 상태: 더 진한 빨강 */
font: 300 14pt &quot;Umpush&quot;;
color: rgb(255, 255, 255);
border: none;
border-radius: 14px; /* ✅ 동글동글 */
padding: 10px 18px; /* 버튼 크기/여백 */
}
</string>
</property>
<property name="text">
<string>시작하기</string>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>