I have a class based on QGraphicsView. I want to make the clicks on WASD move the visible scene to the appropriate direction. The task is simple and clear, wrote the following code:
GameScene::GameScene(QGraphicsScene *scene, QWidget *parent) : QGraphicsView(scene, parent) {
_scene = scene;
_resolution = new QPointF(1920, 1080);
_position = new QPointF(100, 100);
setSceneRect(_position->x(), _position->y(), _resolution->x(), _resolution->y());
installEventFilter(this);
scene->addItem(new QGraphicsLineItem(510, 510, 990, 990)); }
void GameScene::keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_W:
_position->setY(_position->y() - 100);
break;
case Qt::Key_S:
_position->setY(_position->y() + 100);
break;
case Qt::Key_A:
_position->setX(_position->x() - 100);
break;
case Qt::Key_D:
_position->setX(_position->x() + 100);
break;
default:
break;
}
setSceneRect(_position->x(), _position->y(), _resolution->x(), _resolution->y());
update();
}
This code even works, but there is one problem: the scene does not always respond to the setSceneRect function (checked through the debagger signal reaches, the values are sent to the function correct), that is, the key is pressed, the keyPressEvent function is executed, the desired case is selected, the values are updated, but the setSceneRect function does not do anything. What could be the problem?
Related
Working on a school project, but I'm completely stuck. I have to write a paint application, in which you can draw Rectangles & Ellipses, select them, group them, move and resize them. After that I have to start implementing several design patterns.
I have to following problem. I have an abstract class Figure which inherits QRect, then there are the classes Rectangle, Ellipse and Group which all inherit the Figure class.
I am able to resize a single figure (ellipse or shape), by clicking on the corner of a selected shape, but the problem is resizing a group which contains several other figures (group, rectangle or ellipse), they have to resize relatively to their parent figure:
Mouse move event
void MainWindow::OnPaintBoxMouseMove(PaintBox *sender, QMouseEvent *event) {
//Mouse move event based on which tool is selected
switch(tool){
//When rectangle tool is selected, update rectangle size to mouse position
//in command
case rectangle: {
tempCmd->update(event->pos());
break;
}
//When ellipse tool is selected, update ellipse size to mouse position
//in command
case ellipse: {
tempCmd->update(event->pos());
break;
}
//When select tool is selected
case select: {
//If moving is selected, update shape in command
if (moving){
sRect->move(startPoint,event->pos());
tempCmd->update(event->pos());
startPoint = event->pos();
}
//If resizing is selected, update shape in command
else if (resizing){
sRect->resize(startPoint,event->pos());
tempCmd->update(event->pos());
startPoint = event->pos();
}
//Otherwise update size of selection rectangle
else if (sRect != nullptr) {
sRect->updateDimensions(event->pos());
}
break;
}
}
paintBox->update();
}
Resize method in resize command
called 'update' here
void ResizeCmd::update(QPoint mousePosition) {
switch (direction){
case 1: {
_figure->setTopLeft(_figure->topLeft()+mousePosition-startMousePosition);
break;
}
case 2: {
_figure->setTopRight(_figure->topRight()+mousePosition-startMousePosition);
break;
}
case 3: {
_figure->setBottomLeft(_figure->bottomLeft()+mousePosition-startMousePosition);
break;
}
case 4: {
_figure->setBottomRight(_figure->bottomRight()+mousePosition-startMousePosition);
break;
}
}
startMousePosition = mousePosition;
}
So the question is, how am I going to implement the resize method in the group class, which may contain other child groups as well?
I think you are looking for something like this Repo :
https://github.com/cl0ne/resizer-item
For doing your project you should use graphics libs of Qt like QGraphicsItem ,QGraphicsScene, and QGraphicsView
you shouldn't separate all your Items and write a resizer.
this repo is general for all QGraphicsItems which means that If you want Rectangle you should define one QGraphicsRectItem and add this resizer to it like this :
QGraphicsScene *scene = new QGraphicsScene(this);
QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
item->setPos(10, 10);
item->setFlag(QGraphicsItem::ItemIsMovable);
item->setPen(QColor(102, 102, 102));
item->setBrush(QColor(158, 204, 255));
scene->addItem(item);
GraphicsItemResizer *resizer = new GraphicsItemResizer(item);
resizer->setBrush(QColor(64, 64, 64));
resizer->setMinSize(QSizeF(30, 30));
resizer->setTargetSize(item->boundingRect().size());
resizer->setHandlersIgnoreTransformations(true);
QObject::connect(
resizer,
&GraphicsItemResizer::targetRectChanged,
[item](const QRectF & rect)
{
QPointF pos = item->pos();
item->setPos(pos + rect.topLeft());
QRectF old = item->rect();
item->setRect(QRectF(old.topLeft(), rect.size()));
}
);
ui->graphicsView->setScene(scene);
I have a QComboBox with several choices on a QToolBar. Every choice of the QComboBox will open a specific dialog window.
The problem I have is that after I choose the preferred index on the combobox no dialogs opens up. For simplicity of the example I am linking the same dialog to all choices:
dredgewindow.h
This is the header file
namespace Ui {
class DredgeWindow;
}
class DredgeWindow : public QMainWindow
{
Q_OBJECT
public:
explicit DredgeWindow(QWidget *parent = nullptr);
~DredgeWindow();
private:
Ui::DredgeWindow *ui;
DredgeDB *mDredgeDB;
};
dredgewindow.cpp
DredgeWindow::DredgeWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::DredgeWindow)
{
ui->setupUi(this);
QComboBox* myComboBox = new QComboBox;
ui->toolBarControls->addWidget(myComboBox);
myComboBox->addItem("Please Select");
myComboBox->addItem("Bucket");
myComboBox->addItem("Scow");
myComboBox->addItem("Hopper Dredger");
switch(myComboBox->currentIndex()){
case 0:
// do nothing
break;
case 1:
// Go to Bucket
mDredgeDB = new DredgeDB();
mDredgeDB->show();
break;
case 2:
// Go to Scow...
mDredgeDB = new DredgeDB();
mDredgeDB->show();
break;
case 3:
// Go to Hopper Dredger
mDredgeDB = new DredgeDB();
mDredgeDB->show();
default:
break;
}
}
DredgeWindow::~DredgeWindow()
{
delete ui;
}
So far I have been trying to trigger the opening of the dialogs using the combobox but as soon as I release the mouse (and therefore I switch - case) I expect the dialog to open but nothing happens. This source was useful even though it was not in c++. But still I used it to understand the general approach.
This approach triggers the combobox and set it active but other than that there is no specific indication.
Thanks in advance for pointing to the right direction for solving this problem.
You have to connect QComboBox::activated() signal to some slot.
Signals & Slots in Qt5
New Signal Slot Syntax
qOverload<>
This signal is sent when the user chooses an item in the combobox. The
item's index is passed. Note that this signal is sent even when the
choice is not changed. If you need to know when the choice actually
changes, use signal currentIndexChanged().
Note: Signal activated is overloaded in this class. To connect to this
signal by using the function pointer syntax, Qt provides a convenient
helper for obtaining the function pointer - qOverload<>.
class DredgeWindow : public QMainWindow
{
Q_OBJECT
...
private slots:
void on_combo_index_activated(int index);
...
};
DredgeWindow::DredgeWindow(QWidget *parent)
: QMainWindow(parent)
{
...
connect(myComboBox, QOverload<int>::of(&QComboBox::activated),
[=](int index) { on_combo_index_activated(index); });
...
}
void DredgeWindow::on_combo_index_activated(int index)
{
switch (index)
{
case 0:
// do nothing
break;
case 1:
// Go to Bucket
mDredgeDB = new DredgeDB();
mDredgeDB->show();
break;
case 2:
// Go to Scow...
mDredgeDB = new DredgeDB();
mDredgeDB->show();
break;
case 3:
// Go to Hopper Dredger
mDredgeDB = new DredgeDB();
mDredgeDB->show();
default:
break;
}
}
I have a QWidget-based overlay widget which should paint some text and take place over the central widget of my application. The problem is that I can't set background of overlay widget to be transparent. What I already tried:
setPalette(Qt::transparent);
setAttribute( Qt::WA_TranslucentBackground, true );
setAttribute( Qt::WA_OpaquePaintEvent, true );
setAutoFillBackground(false);
setStyleSheet("QWidget{background-color: transparent;}");
setAttribute(Qt::WA_NoSystemBackground);
My best guess to show an overlay widget, is convert the widget to a window, resize it to it's contents and move them to the desired position manually.
MainWindow Example, showing the overlay widget in the center of the video widget:
Mwindow::Mwindow()
{
widget = new Widget(this);
}
void Mwindow::widgetSizeMove()
{
if (widget->width() <= videoWidget->width() && widget->height() <= videoWidget->height())
{
widget->setWindowOpacity(1); // Show the widget
QPoint p = videoWidget->mapToGlobal(videoWidget->pos());
int x = p.x() + (videoWidget->width() - widget->width()) / 2;
int y = p.y() + (videoWidget->height() - widget->height()) / 2;
widget->move(x, y);
widget->raise();
}
else
{
widget->setWindowOpacity(0); // Hide the widget
}
}
bool Mwindow::event(QEvent *event)
{
switch (event->type())
{
case QEvent::Show:
widget->show();
QTimer::singleShot(50, this, SLOT(widgetSizeMove()));
//Wait until the Main Window be shown
break;
case QEvent::WindowActivate:
case QEvent::Resize:
case QEvent::Move:
widgetSizeMove();
break;
default:
break;
}
return QMainWindow::event(event);
}
Widget Example:
Widget::Widget(QWidget *parent) : QWidget(parent)
{
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_TransparentForMouseEvents);
}
void Widget::paintEvent(QPaintEvent*)
{
QPainter p(this);
QString text = "Some foo goes here";
QFontMetrics metrics(p.font());
resize(metrics.size(0, text));
p.drawText(rect(), Qt::AlignCenter, text);
}
Example when showing a video with LibVLC:
On Linux works with:
setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_TransparentForMouseEvents);
The best solution is provided by Gökmen Göksel in one of the comments of this article
setStyleSheet("background-color: rgba(0,0,0,0)");
I want to show or hide items in QStackedWidget. When I press Enter button it should show a stacked element and when I press say a left button it should hide.
I use QStackedWidget and QListWidget. My code:
mymainwindow.h:
#ifndef MYMAINWINDOW_H
#define MYMAINWINDOW_H
class mymainwindow : public QMainWindow
{
Q_OBJECT
public:
mymainwindow();
protected:
void keyPressEvent(QKeyEvent *event);
private:
QStackedWidget *stack;
QListWidget *list;
QVBoxLayout *vertical;
QWidget *widget;
};
#endif
mymainwindow.cpp:
#include "mymainwindow.h"
mymainwindow::mymainwindow() : QMainWindow()
{
stack = new QStackedWidget();
list = new QListWidget();
stack->addWidget(new QLineEdit("Hello U have clicked the first menu"));
stack->addWidget(new QLineEdit("Second ListWidget Item"));
stack->addWidget(new QLineEdit("Last Widget Item"));
widget = new QWidget();
QLabel *label = new QLabel("Main Window");
list->addItem("New Item 1");
list->addItem("New Item 2");
list->addItem("New Item 3");
list->setFixedSize(200,100);
QVBoxLayout *vertical = new QVBoxLayout();
vertical->addWidget(label);
vertical->addWidget(list);
vertical->addWidget(stack);
widget->setLayout(vertical);
setCentralWidget(widget);
}
void mymainwindow::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Down:
connect(list, SIGNAL(currentRowChanged(int)), stack, SLOT(setCurrentIndex(int)));
break;
case Qt::Key_Up:
connect(list, SIGNAL(currentRowChanged(int)), stack, SLOT(setCurrentIndex(int)));
break;
case Qt::Key_Left:
break;
}
}
You will need to handle the Key_Left and Key_Enter cases in your key press event handler. It seems that you would simply like to show and hide the stackwidget based on the press of those two buttons. This is a simple QWidget operation, and the problem is not much related to QStackedWidget.
You would need to change the key press event code as follows:
void mymainwindow::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Down:
connect(list,SIGNAL(currentRowChanged(int)),stack,SLOT(setCurrentIndex(int)));
break;
case Qt::Key_Up:
connect(list,SIGNAL(currentRowChanged(int)),stack,SLOT(setCurrentIndex(int)));
break;
case Qt::Key_Left:
stack->show(); // <---- Added
break;
case Qt::Key_Enter: // <---- Added
stack->hide(); // <---- Added
break; // <---- Added
}
}
i think of to write primitive snake. i have window where program paints random lines.
but i can't think up how to catch pressed key to change direction of painted line.
class QUpdatingPathIte : public QGraphicsPathItem
{
void advance(int phase)
{
if (phase == 0)
return;
int x,y;
int w,a,s,d;
char c;
//
// HOW TO MAKE THIS HERE? (i had done early in console application)
// but i can't do this in GUI , what should i do?
scanf("%c",&c);
if (c=='w')
{ x=-20; y=0; }
else if (c=='s')
{ x=20; y=0; }
else if (c=='a')
{ x=0; y=-20; }
else if(c=='d')
{ x=0; y=20; }
QPainterPath p = path();
p.lineTo(x, y);
setPath(p);
}
};
#include <QtGui/QApplication>
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene s;
QGraphicsView v(&s);
QUpdatingPathIte item;
item.setPen(QPen(QColor("black")));
s.addItem(&item);
v.show();
QTimer *timer = new QTimer(&s);
timer->connect(timer, SIGNAL(timeout()), &s, SLOT(advance()));
timer->start(500);
return a.exec();
}
QGraphicsView inherits from from QWidget because it inherits from QAbstractScrollArea. You should create a custom subclass of QGraphicsView and just override the function keyPressEvent(). Example:
class SnakeView : public QGraphicsView
{
protected:
keyPressEvent(QKeyEvent *e)
{
// Do something
// Otherwise pass to the graphics view
QGraphicsView::keyPressEvent(e)
}
};
Then rather than creating a QGraphicsView object you would create a SnakeView object.
QGraphicsView reimplements keyPressEvent and will forward events to the QGraphicsScene. QGraphicsScene supports key presses through its keyPressEvent handler. By default the QGraphicsScene will forward that key press to the current QGraphicsItem through its keyPressEvent function.
Given the above, what you likely want to do is reimplement the keyPressEvent handler in your QUpdatingPathItem:
void QUpdatingPathItem::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Key::Key_A: /* do something useful */; break;
case Key::Key_S: /* do something useful */; break;
case Key::Key_W: /* do something useful */; break;
case Key::Key_D: /* do something useful */; break;
}
}
I couldn't really follow what you were trying to do in your advance method, so I can't offer much in the way of a suggested handler.
Besides implementing QObject::installEventFilter, you could also subclass any of QGraphicsView, QGraphicsScene, or QGraphicsItem and override the keyPressEvent function.
thanks you all. Then I didn't reach my aim. But time went on and I've come to the same problem and now I solved it.
There are right and worth answers but then I wasn't good enough to put them together because no one was full and sufficient. Now I did it so:
HEADER FILE "header.h"
class MyGraphicView: public QGraphicsView
{
Updating *m_update;
public:
MyGraphicView(Updating *update);
void keyPressEvent(QKeyEvent *event);
};
#include "header.h"
void GraphicView::keyPressEvent(QKeyEvent *event)
{
switch ( event->key())
{case Qt::Key_Up:
m_update->move_up();
break;
case Qt::Key_Down:
m_update->move_down();
break;
case Qt::Key_Left:
{ m_update->move_right();
break;
}
case Qt::Key_Right:
{ m_update->move_left();
break;
}
}
}
Thanks again!
Your choice of QGraphicsView isn't very fortunate.
Basically, when you are implementing some custom painted graphical item (the graphic area of the game is this), you should create your own widget and simply implement the QWidget::keyPressEvent() method.