UE5/C++ Progress Bar: Change a background image during runtime - c++

I'm quite new to UE, so I'm seeking for some help! Currently I'm working on HUD and faced some issues with Progress Bars. The goal is pretty simple, I need to change a background image of Progress Bar during runtime. According to my logging everything is working but it doesn't show any changes on Screen. I appreciate any help!
My code:
.h
UPROPERTY(EditAnywhere, meta=(BindWidget))
class UProgressBar* IconSlot1;
UPROPERTY(EditAnywhere, meta=(BindWidget))
class UProgressBar* IconSlot2;
UPROPERTY(EditAnywhere, meta=(BindWidget))
class UProgressBar* IconSlot3;
UPROPERTY(EditAnywhere)
TArray<class UIconSlot*> IconArr;
UPROPERTY(VisibleAnywhere)
TArray<class UProgressBar*> SlotArr;
.cpp
void UPlayerUI::SetSlotIcon(const TEnumAsByte<EDamageClass>& DamageClass)
{
const auto EmptySlot = GetFreeSlot();
const auto Icon = GetImage(DamageClass);
if (IsValid(EmptySlot) && IsValid(Icon))
{
FProgressBarStyle Style = EmptySlot -> GetWidgetStyle();
if (Style.BackgroundImage.GetResourceObject() == nullptr)
{
FSlateImageBrush Brush = FSlateImageBrush(Icon, FVector2d(100, 100));
Style.SetBackgroundImage(Brush);
UE_LOG(LogTemp, Warning, TEXT("Image slot is set"));
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Image slot isn't empty"));
return;
}
Slots.Add(EmptySlot, DamageClass);
}
}
UProgressBar* UPlayerUI::GetFreeSlot()
{
for (const auto IconSlot : SlotArr)
{
if (!Slots.Contains(IconSlot))
{
UE_LOG(LogTemp, Warning, TEXT("Slot found"));
return IconSlot;
}
}
return nullptr;
}
UTexture2D* UPlayerUI::GetImage(const TEnumAsByte<EDamageClass>& DamageClass)
{
for (const auto IconImage : IconArr)
{
if (IconImage -> DamageClass == DamageClass && !Icons.Contains(IconImage))
{
UE_LOG(LogTemp, Warning, TEXT("Image found"));
Icons.Add(IconImage, DamageClass);
return IconImage -> Image;
}
}
return nullptr;
}

Resolved it by adding these lines:
Style = Style.SetBackgroundImage(Brush);
EmptySlot -> SetWidgetStyle(Style);

Related

Box2D Contact Listener Collision Only Works Intermittently

I have a dynamic body colliding with a dynamic body and a simple contact listener class shown below:
void myContactListener::BeginContact(b2Contact* contact)
{
void* bodyUserData = contact->GetFixtureA()->GetBody()->GetUserData();
if (bodyUserData)
{
static_cast<EnemyEntity*>(bodyUserData)->startContact();
}
bodyUserData = contact->GetFixtureB()->GetBody()->GetUserData();
if (bodyUserData)
{
static_cast<EnemyEntity*>(bodyUserData)->endContact();
}
}
void myContactListener::EndContact(b2Contact* contact)
{
void* bodyUserData = contact->GetFixtureA()->GetBody()->GetUserData();
if (bodyUserData)
static_cast<EnemyEntity*>(bodyUserData)->endContact();
bodyUserData = contact->GetFixtureB()->GetBody()->GetUserData();
if (bodyUserData)
static_cast<EnemyEntity*>(bodyUserData)->endContact();
}
Whenever I move either of the dynamic bodies into each other, a collision is always detected the first time as well as a stop of collision. However, if I try to collide them once again, it often doesn't detect any collision after the first time. It does sometimes though. What can I do to fix this? Nothing else in my code touches any Box2D code.
Here is my contact listener header file:
class myContactListener : public b2ContactListener
{
private:
public:
// Contact listener methods
void BeginContact(b2Contact* contact);
void EndContact(b2Contact* contact);
};
And the EnemyEntity Box2D method:
void EnemyEntity::createStarBox2DCollision(b2World *world)
{
enemyType = EnemyType::Star;
def.type = b2_dynamicBody;
def.position.Set(1000, 1000);
body = world->CreateBody(&def);
box.SetAsBox(rectShapeSize.x / 2, rectShapeSize.y / 2);
fixtureDef.shape = &box;
fixtureDef.density = 1.0;
fixtureDef.friction = 0.3;
body->CreateFixture(&fixtureDef);
body->SetUserData(this);
}
It looks like you should have
void myContactListener::BeginContact(b2Contact* contact)
{
...
static_cast<EnemyEntity*>(bodyUserData)->startContact();
}
}
rather than
void myContactListener::BeginContact(b2Contact* contact)
{
...
static_cast<EnemyEntity*>(bodyUserData)->endContact();
}
}
for both bodies, rather than only for A

How to enable a dynamic whatsthis-string in Qt using QWhatsThis?

In order to have a dynamic what's this string for a widget in Qt the following almost works (following the documentation http://doc.qt.io/qt-5/qtwidgets-widgets-tooltips-example.html ):
class MyEdit : public QLineEdit {
Q_OBJECT
public:
bool event(QEvent*e) {
if (e && e->type() == QEvent::WhatsThis) {
if (QHelpEvent *helpEvent = reinterpret_cast<QHelpEvent *>(e)) {
QWhatsThis::showText(helpEvent->globalPos(), "My text...");
return true;
}
}
return QLineEdit::event(e);
}
};
Activating what's this for the window and clicking on the widget shows "My text" (the actual text is more complicated).
Issues:
Activating what's this for the window and hovering over this widget shows a dead cursor
Shift-F1 does not work inside the widget.
The first issue could be worked around by calling setWhatsThis("Dummy text"); with a non-empty string, but it feels like a hack and Shift-F1 in this widget shows "Dummy text".
Is there a non-hack way of handling it - especially so that it will not be broken by updates?
In case someone else stumbles on this issue the following seems to handle the Shift-F1:
class MyEdit : public QLineEdit {
Q_OBJECT
public:
bool event(QEvent*e) {
if (e && e->type() == QEvent::WhatsThis) {
if (QHelpEvent *helpEvent = reinterpret_cast<QHelpEvent *>(e)) {
QWhatsThis::showText(helpEvent->globalPos(), "My text...");
return true;
}
}
if (e && e->type() == QEvent::KeyPress) {
if (QKeyEvent *qk = reinterpret_cast<QKeyEvent *>(e)) {
if (qk->key() == Qt::Key_F1 && (qk->modifiers()&Qt::ShiftModifier)) {
QWhatsThis::showText(w.mapToGlobal(w.inputMethodQuery(Qt::ImCursorRectangle).toRect().center()), "My text...");
qk->accept();
return true;
}
}
}
return QLineEdit::event(e);
}
};
and combined with calling setWhatsThis("Dummy text"); also the hovering-part. However, it is not an elegant solution.

Undo stack remains empty in Qt

I am trying to undo/redo in multi document interface. I have different entities. Each entity has its own class. I have used UndoGroup but when I unable to push them to undoStack dont know whats's wrong there. Can anyone help me to solve the issue.
cadgraphicscene.cpp
CadGraphicsView::CadGraphicsView()
{
undoStack = new QUndoStack(this);
}
QUndoStack *CadGraphicsView::m_undoStack() const
{
return undoStack;
}
void CadGraphicsView::showUndoStack()
{
undoView = 0;
// shows the undoStack window
if (undoView == 0)
{
undoView = new QUndoView(undoStack);
undoView->setWindowTitle("Undo Stack");
}
undoView->show();
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
m_undoGroup = new QUndoGroup(this);
QAction *undoAction = m_undoGroup->createUndoAction(this);
undoAction->setShortcut(QKeySequence::Undo);
QAction *redoAction = m_undoGroup->createRedoAction(this);
redoAction->setShortcut(QKeySequence::Redo);
menuEdit->insertAction(menuEdit->actions().at(1), undoAction);
menuEdit->insertAction(undoAction, redoAction);
menuEdit->addAction(undoAction);
menuEdit->addAction(redoAction);
undoAction->setEnabled(true);
redoAction->setEnabled(true);
}
void MainWindow::updateActions()
{
CadGraphicsView *view = currentDocument();
m_undoGroup->setActiveStack(view == 0 ? 0 : view->m_undoStack());
}
void MainWindow::addDocument(CadGraphicsView *view)
{
m_undoGroup->addStack(view->m_undoStack());
connect(view->m_undoStack(), SIGNAL(indexChanged(int)), this, SLOT(updateActions()));
connect(view->m_undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(updateActions()));
setCurrentDocument(view);
}
void MainWindow::setCurrentDocument(CadGraphicsView *view)
{
mdiArea->currentSubWindow();
}
CadGraphicsView *MainWindow::currentDocument() const
{
return qobject_cast<CadGraphicsView *>(mdiArea->parentWidget());
}
I am confused with why I am not able to push entities to undoStack. Please help me to solve this issue
I think the problem is in these two functions (see the inline comments):
void MainWindow::setCurrentDocument(CadGraphicsView *view)
{
// The view argument is not used at all. You do not set anything here.
mdiArea->currentSubWindow();
}
CadGraphicsView *MainWindow::currentDocument() const
{
// mdiArea->parentWidget() returns the MainWindow, so this function
// always returns 0.
return qobject_cast<CadGraphicsView *>(mdiArea->parentWidget());
// You should write it as
// return qobject_cast<CadGraphicsView *>(mdiArea->activeSubWindow()->widget());
}

Error while trying Undo Redo

I am trying to do undo/redo on multidocument interface but facing following error:
no matching function for call to 'qobject_cast(QMdiSubWindow*&)'
return qobject_cast<CadGraphicsView *>(activeSubWindow);
My code to above function is as follows:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
m_undoGroup = new QUndoGroup(this);
QAction *undoAction = m_undoGroup->createUndoAction(this);
undoAction->setShortcut(QKeySequence::Undo);
QAction *redoAction = m_undoGroup->createRedoAction(this);
redoAction->setShortcut(QKeySequence::Redo);
menuEdit->insertAction(menuEdit->actions().at(0), undoAction);
menuEdit->insertAction(undoAction, redoAction);
menuEdit->addAction(undoAction);
menuEdit->addAction(redoAction);
updateActions();
}
void MainWindow::setCurrentDocument()
{
mdiArea->currentSubWindow();
}
void MainWindow::addnewFile()
{
m_undoGroup->addStack(view->scene->undoStack());
connect(view->scene->undoStack(), SIGNAL(indexChanged(int)), this, SLOT(updateActions()));
connect(view->scene->undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(updateActions()));
}
void MainWindow::updateActions()
{
CadGraphicsScene *scene = currentDocument();
m_undoGroup->setActiveStack(scene == 0 ? 0 : scene->undoStack());
}
CadGraphicsView *MainWindow::currentDocument() const
{
if (QMdiSubWindow *activeSubWindow = mdiArea->currentSubWindow())
return qobject_cast<CadGraphicsView *>(activeSubWindow);
return 0;
}
void MainWindow::newFile()
{
// creates a new file
createMdiView();
view->newFile();
addnewFile();
curFileName = tr("Document %1").arg(++fileNumber);
view->setWindowTitle(curFileName);
view->scene->installEventFilter(this);
view->show();
isEntitySelected = false;
}
CadGraphicsView *MainWindow::createMdiView()
{
// creates a graphicsView and add it to the MDI window
view = new CadGraphicsView;
QMdiSubWindow *w = mdiArea->addSubWindow(view);
mdiArea->setActiveSubWindow(w);
windowViewList.append(qMakePair(w, view));
return view;
}
Can you please help me to solve the error. I have added the basic idea how it is working.
Got the solution to my problem. I need to change the currentDocument function as follows:
CadGraphicsView *MainWindow::currentDocument() const
{
return qobject_cast<CadGraphicsView *>(mdiArea->parentWidget());
}

Connecting SLOTS in Qt

I have a context menu and edit menu. I want to connect context menu slots with the edit menu.
Like EDIT menu has menu Items: cut, copy and paste
My context menu slots are:
void CadGraphicsScene::cut(getEntity *obj)
{
// id of item pasted is kept same as that of the item being cut
removeItem(obj);
clipboardStack::instance()->push(obj->clone(contextItemId));
}
void CadGraphicsScene::copy(getEntity *obj)
{
// id of item pasted is one more than total number of items in the scene
clipboardStack::instance()->push(obj->clone(++id));
}
void CadGraphicsScene::paste(const QPointF &pos)
{
// gets the items cut/copy from clipboardStack to paste
getEntity *pasteEntity = clipboardStack::instance()->pop();
if (pasteEntity->type() == Point::Type)
{
Point *itemPtr = dynamic_cast<Point *>(pasteEntity);
itemPtr->position = pos;
drawEntity(itemPtr);
}
if (pasteEntity->type() == Line::Type)
{
Line *itemPtr = dynamic_cast<Line *>(pasteEntity);
itemPtr->startP = pos;
/* calculates difference between startP of line being cut/copy and line
* being pasted for proper pasting of line
*/
differenceX = itemPtr->startP.x() - lineStartPoint.x();
differenceY = itemPtr->startP.y() - lineStartPoint.y();
itemPtr->endP = QPointF(lineEndPoint.x() + differenceX,
lineEndPoint.y() + differenceY);
drawEntity(itemPtr);
}
if (pasteEntity->type() == Circle::Type)
{
Circle *itemPtr = dynamic_cast<Circle *>(pasteEntity);
itemPtr->centerP = pos;
drawEntity(itemPtr);
}
if (pasteEntity->type() == Ellipse::Type)
{
Ellipse *itemPtr = dynamic_cast<Ellipse *>(pasteEntity);
itemPtr->p1 = pos;
drawEntity(itemPtr);
}
if (pasteEntity->type() == Text::Type)
{
Text *itemPtr = dynamic_cast<Text *>(pasteEntity);
itemPtr->position = pos;
drawEntity(itemPtr);
}
setMode(NoMode);
}
//context menu actions
void CadGraphicsScene::menuAction(QAction *action)
{
if (action == cutAction)
{
cut(static_cast<getEntity *>(contextItem));
}
else if (action == copyAction)
{
copy(static_cast<getEntity *>(contextItem));
}
else if (action == pasteAction)
{
paste(contextPosition);
}
}
How can the same be done from edit menu? How can the same slots be used?
for cut in the edit menu I made another slot:
void CadgraphicsScene::cut()
{
cutAction
}
connect(actionCut, SIGNAL(triggered), this, SLOT(cut()));
If you want to reuse the same slots in your application's Edit menu, just use existing cutAction, copyAction and pasteAction actions when constructing it. So if you has established the connections for that actions the same slots will be called both when user triggers actions from context menu and from Edit menu.