I have two "QMainWindows" A and B.
A has a pointer to B as a class member (and creates B dynamically on constructor). The program implementation is in two different monitors. 'A' is shown on monitor 'X' and 'B' is shown on monitor 'Y'.
'A' controls what is shown on 'B', and since I have them in two different monitors, in 'A' I would like to see whats happening on 'B' (kind of a 'screen capture on real time'). Is there any way to do this?
I've thought to make a "QWidget *clone = B->ui->centralWidget;" on 'A's constructor, since in 'B' I have all the information under a 'QWidget centralWidget', but it doesn't seem to work.
Any illuminating idea would be deeply apreciated!
You should be able to use QPixmap::grabWindow and a QTimer. QPixmap::grabWindow returns a QPixmap that you can simply display on e.g. a QLabel
Dummy header file for the class that displays
class MainWindow: public QMainWindow, public Ui::MainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = NULL);
~MainWindow() {};
public slots:
void update();
private:
MainWindow2 *window;
QTimer *timer;
};
Implementation
MainWindow::MainWindow(QWidget *parent)
{
setupUi(this);
window = new MainWindow2;
window->show();
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(5000);
}
void MainWindow::update()
{
if( window ) {
QPixmap pixmap = QPixmap::grabWindow( window->winId() );
label->setPixmap(pixmap);
}
}
EDIT: added a check to make sure that window is not NULL before asking for its window ID.
This way a screenshot of the child window will be shown on in a QLabel of the parent window and will be updated every 5 seconds (which you can obviously adjust to your needs). Does this cover your use case?
Related
I have the following code:
roslaserscandoialog.h
public:
explicit ROSLaserScanDialog(QWidget *parent = nullptr);
~ROSLaserScanDialog();
QListWidgetItem *createItemFromAction(const QAction* action);
private slots:
void on_listWidget_itemClicked(QListWidgetItem *item);
private:
Ui::ROSLaserScanDialog *ui;
QAction *mAddMsgs;
QAction *mDeleteMsgs;
roslaserscandoialog.cpp
ROSLaserScanDialog::ROSLaserScanDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ROSLaserScanDialog)
{
ui->setupUi(this);
connect(ui->listWidget,SIGNAL(on_listWidget_itemClicked(QListWidgetItem*)),this,SLOT(createItemFromAction(QListWidgetItem*)));
}
QListWidgetItem *ROSLaserScanDialog::createItemFromAction(const QAction *action)
{
Q_ASSERT( action );
QListWidgetItem *mAddMsgs = new QListWidgetItem();
mAddMsgs->setText( action->text() );
mAddMsgs->setToolTip( action->toolTip() );
mAddMsgs->setIcon( action->icon() );
// ...
return mAddMsgs;
}
void ROSLaserScanDialog::on_listWidget_itemClicked(QListWidgetItem *item)
{
mAddMsgs = new QAction(QIcon(":ros.png"), tr("Add New Message"), this);
mDeleteMsgs = new QAction(QIcon(":remove_item.png"), tr("Remove Message"), this);
}
What I have done so far:
I came across this post and also this one. Very useful as I set up my project in a very similar way, but nothing happens when I try to click on the QListWidget.
I know that in order to trigger the action I have to go to the slot called itemClicked as I did on the above code provided.
On the official documentation I was trying to apply what is advised but I don't know why nothing happens.
Please point to the right direction for solving this problem.
Look at console output, there should a warning about connect failing. If you look at your code, the reason should be pretty obvious. Consider
SLOT(createItemFromAction(QListWidgetItem*))
versus your method which isn't even a slot
QListWidgetItem *createItemFromAction(const QAction* action);
See the difference?
And then you have this slot:
void on_listWidget_itemClicked(QListWidgetItem *item);
which you are trying to use as a signal
SIGNAL(on_listWidget_itemClicked(QListWidgetItem*))
That obviously won't work.
It's a bit unclear what you want to happen when an item is clicked, but maybe you just should call createItemFromAction directly from the on_listWidget_itemClicked.
Additionally, add debug print or use breakpoint to verify that the on_listWidget_itemClicked is actually called when you click an item. If not, then you are missing connecting the relevant signal from your list view, ie. ui->setupUi(this); does not have that connect (in other words you did not do the connection in the GUI Designer).
I notice that QML's StatusBar type doesn't include the SizeGrip like QStatusBar does.
In order to get a size grip, I had to instead embed the QML into a QMainWindow with a QStatusBar. Although this works, it complicates the rest of the app and it's not really the design I'm after.
Would it be possible to implement the QStatusBar directly in QML by subclassing it and how would QML be able to recognise/use the SizeGrip?
EDIT:
I've attempted to derive QQuickPaintedItem to try and render a QStatusBar in QML, but so far haven't had any luck. An error is being triggered in the render call: ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 399d3d8. Receiver '' (of type 'QStatusBar') was created in thread 8c9f00", file kernel\qcoreapplication.cpp, line 553
.h
class WindowStatusBar : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit WindowStatusBar(QQuickItem *parent = 0);
virtual ~WindowStatusBar();
void paint(QPainter *painter);
protected:
QStatusBar *statusBar_;
};
.cpp
WindowStatusBar::WindowStatusBar(QQuickItem *parent)
: QQuickPaintedItem(parent)
, statusBar_(NULL)
{
setOpaquePainting(true);
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::AllButtons);
statusBar_ = new QStatusBar;
}
WindowStatusBar::~WindowStatusBar()
{
delete statusBar_;
}
void WindowStatusBar::paint(QPainter *painter)
{
statusBar_->render(painter, QPoint(), QRegion(),
QStatusBar::DrawWindowBackground | QStatusBar::DrawChildren);
}
Yes, you can derive your own statusbar QML type from StatusBar or you can use the standard QML StatusBar with a contentItem that you designed. To implement the size grip you would put a MouseArea at the right border- in the onPositionChanged you would emit a signal that is interpreted by the mainwindow as a resize command. Avoiding feedback loops (because resizing the main window may change the position in the MouseArea) is left as an exercise for the reader.
I want to have a list view just like the one in Windows' file explorer: The data is shown into columns. So I set up a QListView with the code below.
It looks totally the same (see picture).
But one major drawback: When resizing the window, the wrapping is very slow. On the contrary, the Window's file browser is very fast.
How can I speed up the wrapping in the QListView?
h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void resizeEvent(QResizeEvent *);
private:
Ui::MainWindow *ui;
};
cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QStandardItemModel* m=new QStandardItemModel;
for (int i=100;i<1000;++i){
m->insertRow(i-100,new QStandardItem(QString::number(i).repeated(5)+" "));
}
ui->listView->setModel(m);
ui->listView->setWrapping(true);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::resizeEvent(QResizeEvent *e)
{
QMainWindow::resizeEvent(e);
ui->listView->setWrapping(ui->listView->isWrapping());
}
I tried your code under Linux with Qt 5.5.1 and wrapping is fast and instant.
What Qt version are you using? Qt 4.x has some performance issues under windows.
You can speed things up with NoAntialias
QFont fnt;
fnt.setStyleStrategy(QFont::NoAntialias);
ui->listView->setFont(fnt);
If your list grows bigger and you wan't to insert new data you will get bad performance. You should avoid QStandardItemModel for large sets of data.
You don't need to set wrapping every time the widget is resized. Qt is looking for the isWrapping property when resized and decides if a new segment is needed. You just need to set the flow property LeftToRight or TopToBottom and the isWrapping property once when the widget is created. Remove the code from the resizeEvent.
Also you can look at the layoutMode property for the performance purposes.
I'm working with Qt (v 5.3) again after sometime away. As a learning exercise I am prototyping a module using QGraphicView in order to understand better how to use it.
The functionality is simple: I have several QGraphicObjects which function like buttons with states and behaviors:
OFF - image panel hidden; default button art
ON - image panel
displayed; highlight button art
At startup all buttons are in an OFF state
A clicked button will toggle it's state (non-Exclusive)
If a different button is already in an ON state, it must be turned off (Exclusive)
Everything is data driven and created dynamically at runtime.
So that is my little learning exercise. What I am trying to sort out is an efficient messaging mechanism to handle the "radio button sometimes" exclusive behavior and sending a message to a group of objects without strongly coupling them.
I've looked at signals and slots but that gets tedious if there are many connections to make.
QSignalMapper which seems somewhat better
QEvent is probably the most solid approach but I've had trouble finding good learning examples.
I am making this more complicated than need be but as I say, it's a learning exercise to get used to Qt again.
So my question (finally): is there an approach I am overlooking or one of these mentioned which would be better (e.g most flexible, maintainable, scalable). Not looking for code per se, but an understanding of how to go about coding something like this using the Qt framework.
Here's an image of the thing:
Signal and slots is the method I would use here. However, rather than connect each button to every other button, you can create an intermediate, Controller class.
The Controller class can either aggregate, or be a parent to all of the buttons.
When a button is created, it connects signals to the controller to inform it of being pressed. The receiving slot in the Controller class is then responsible for turning off any other buttons.
Skeleton code: -
class ButtonController : public QGraphicsObject
{
public slots:
void ButtonPressed();
private:
QList<MyButton*> m_buttonList;
};
void ButtonController::ButtonPressed()
{
foreach(MyButton* pButton, m_buttonList)
{
if(pButton != sender())
{
pButton->Off();
}
}
}
In the constructor of MyButton, assuming the controller is the parent: -
MyButton::MyButton(ButtonController* parent)
: QGraphicsObject(parent);
{
connect(this, &MyButton::pressed(), parent, &ButtonController::ButtonPressed());
...
}
Alternatively, the controller may just hold a list of the buttons, in which case you can do this: -
MyButton::MyButton(QObject* parent, ButtonController* pButtonController)
: QGraphicsObject(parent);
{
connect(this, &MyButton::pressed(), pButtonController, &ButtonController::ButtonPressed());
...
}
In the case of not being able to change the constructor of the buttons, the controller may simply have an AddButton function, in which it creates the connection when the button is added under its control.
Subclass QGraphicsScene
Reimplement mousePressEvent and check is left mouse button pressed.
If so, then get position of mouse by pos() method and get current graphics object under arrow by itemAt() method.
Try to cast it into your needed object, if it was successfully, then change background and other.
Store your graphics objects in vector, in this case you can check every object and decide which object should be turned off.
Fully working example:
header:
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QPoint>
#include <QMouseEvent>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
public slots:
private:
QVector<QGraphicsEllipseItem * > vec;
};
#endif // GRAPHICSSCENE_H
cpp:
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsItem>
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
//set circles in different positions.
vec.push_back(addEllipse(0,0,50,50,QPen(Qt::red),QBrush(Qt::blue)));
vec.push_back(addEllipse(0+100,0+100,50,50,QPen(Qt::red),QBrush(Qt::blue)));
vec.push_back(addEllipse(0+150,0+150,50,50,QPen(Qt::red),QBrush(Qt::blue)));
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
//qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
QGraphicsEllipseItem *ell = qgraphicsitem_cast<QGraphicsEllipseItem *>(item);
if(ell)
{
for(int i = 0; i < vec.size(); i++)
{
if(vec.at(i) != ell)
{
vec.at(i)->setBrush(QBrush(Qt::blue));//disable all other bettons
}
}
ell->setBrush(QBrush(Qt::black));//to chosen circle
qDebug() << "good";
}
else
qDebug() << "not ell" << mouseEvent->scenePos();
}
}
Usage:
GraphicsScene *scene = new GraphicsScene(this);
ui->graphicsView->setScene(scene);
With this case, your circles will be similar to radioButtons. If you want save state of all buttons you can use:
for(int i = 0; i < vec.size(); i++)
vec.at(i)->setData(0,false);
And get state with
if(!ell->data(0).toBool())
//do
I'm trying to create custom widget inheriting QFrame. All works fine, but I'm unable to draw the focus rectangle around my widget. Below is the sample code I use for drawing:
frame.h
class Frame : public QFrame {
Q_OBJECT
public:
Frame(QWidget *parent = 0);
~Frame();
protected:
void paintEvent(QPaintEvent *event);
private:
Ui::Frame *ui;
};
frame.cpp
Frame::Frame(QWidget *parent) :
QFrame(parent),
ui(new Ui::Frame)
{
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
}
Frame::~Frame()
{
delete ui;
}
void Frame::paintEvent(QPaintEvent *event)
{
QFrame::paintEvent(event);
if (hasFocus()) {
QStylePainter painter(this);
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().dark().color();
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
}
}
What I mean by 'unable to draw focus frame' is that when you click a standard widget that accepts focus (let's say QLineEdit), it has a blue rectangle drawn around it. When I click my widget there is no such rectangle drawn. Are there any more things I should do besides setting focusPolicy on my widget?
It might have something to do with the style your app is using. When I try your code with the "gtk" and "cleanlooks" style, no focus rectangle is drawn. With "plastique" and "windows" it is. Since I'm on Linux, I cannot test "windowsxp" and "macintosh". Try running with the -style option and see what happens.
try also
setFocusPolicy(Qt::StrongFocus);
setAttribute( Qt::WA_MacShowFocusRect);