How to capture the current screen image using Qt? - qt

I have a button in my Custom QDialog, I am emitting a signal when pushbutton 1 is clicked
void MyCustomDialog::on_pushButton_1()
{
this->hide(); //i need to hide this window before 'OnButton_1_Clicked' stuffs starts
emit button_1_clicked();
}
In my main window I have connected the slot and created the instance as shown below
void MainWindow::MainWindow()
{
MyCustomDialog *dlg = MyCustomDialog::getInstance(this); //only single instance created
connect(dlg, &MyCustomDialog::button_1_clicked, this, &MainWindow::OnButton_1_Clicked);
}
I am displaying my custom dialog from a function in mainwindow as below
void MainWindow::dispayCustomDialog()
{
MyCustomDialog *dlg = MyCustomDialog::getInstance();
dlg->show();
}
Below shows how my 'OnButton_1_Clicked' slot. In which I am capturing the screenshot using below line
void MainWindow::OnButton_1_Clicked()
{
//capture the screen shot
QScreen *screen = QGuiApplication::primaryScreen();
QPixmap *map = new QPixmap(screen->grabWindow(0));
bool result = map->save("D:/test.jpg", "JPG");
}
Once I captured screen using above function, I can still see my 'MyCustomDialog' in test.jpg file. Qt doc says QGuiApplication::primaryScreen captures the initial state of application. So i think, this is expected in my case. Do we have any other solution to grab screen with current state ?
What I am trying to achieve is grab the screen in OnButton_1_Clicked() function after hiding my 'MyCustomDialog'.

I found a solution. Used a singleslot timer with delay of 500 msec before capturing the screen as below. This wait for my custom dialog to hide properly.
void MainWindow::OnButton_1_Clicked()
{
QTimer::singleShot(500, this, &MainWindow::shootscreen);
}
void MainWindow::shootscreen()
{
//capture the screen shot
QScreen *screen = QGuiApplication::primaryScreen();
QPixmap map = screen->grabWindow(0);
bool result = map.save("D:/test.jpg", "JPG");
}

Related

Geometry information of QRubberBand giving SIGSEGV

I am designing a Qt application using Qt Creator. As a part of the application, I need to be able to get position, height and width information of QRubberBand before I hide() it. For that purpose I tried to use the following logic, which is given in the documentation of QRubberBand:
void Widget::mousePressEvent(QMouseEvent *event)
{
origin = event->pos();
if (!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
rubberBand->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
And defined rubberBand as below in the private section of the header file:
QRubberBand *rubberBand;
After doing that it works well. To go to the next step I defined the following integers as well (private section of the header):
int rubX;
int rubY;
int rubWidth;
int rubHeight;
And I tried to get geometry information before hiding rubberBand in mouseReleaseEvent similar to the following:
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
rubX = rubberBand->x();
rubY = rubberBand->y();
rubHeight = rubberBand->height();
rubWidth = rubberBand->width();
rubberBand->hide();
}
When I added those codes, the program runs, but when I try drawing the rubberBand, the program crashes giving SIGSEGV.
So here are my questions:
Why this is happening?
Is it possible to accomplish my goal by slightly editing the code?
What should I do to get what I want?
I know that I have done a foolish mistake, but I have not found it yet. Do not hesitate to comment if you want to get more information about the question.
From the code and comments you appear to be assuming that the rubberBand member will automatically be initialized to nullptr. However, it's actually uninitialized making the following...
if (!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
result in undefined behaviour.
Change your Widget constructor to include an explicit initialization...
Widget::Widget ()
: rubberBand(nullptr)
{
...
}
Better still, just make rubberBand a non-pointer member...
QRubberBand rubberBand;
and initialize it in the constructor with...
Widget::Widget ()
: rubberBand(QRubberBand::Rectangle, this)
{
...
}

How to show a QDialog in center of a QGraphicsView?

I have a QGraphicsView subclass where, on context menu on an item, I want to show a Properties dialog.
I would like the dialog to be centered in the view...
As I have it now, with no parent, it is shown in the center of the screen.
MyView::MyView(QWidget *parent) : QGraphicsView(parent) {}
void MyView::showProperties()
{
TabDialog *tabDialog = new TabDialog(); // shows in center of screen
// TabDialog *tabDialog = new TabDialog(this); // doesn't show at all
// TabDialog *tabDialog = new TabDialog((QWidget*)this->parent()); // doesn't show at all
tabDialog->setWindowFlags(Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint);
tabDialog->exec();
delete tabDialog;
}
The view is placed in a groupbox... so the parent is the groupbox...
How can I call the dialog using a parent ?
You have to set the dialog to be a top-level window, not simply a child widget that would be embedded in your view. Your setWindowsFlags call resets the relevant flags from the dialog. You need to manually preserve them.
You also should never use exec() to reenter the event loop since this requires that a lot of your other code needs to be reentrant as well.
A minimal fix would look like:
void MyView::showProperties()
{
auto dialog = new TabDialog(this);
dialog->setWindowFlags(Qt::Dialog |
Qt::WindowCloseButtonHint |
Qt::WindowSystemMenuHint);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
}
Perhaps you'd want to cache the dialog:
class MyView : public ... {
QPointer<TabDialog> m_tabDialog; // nulls itself when the dialog perishes
QTimer m_tabDialogTimer;
...
};
MyView::MyView(...) {
m_tabDialogTimer->setSingleShot(true);
...
}
void MyView::showProperties() {
if (! m_tabDialog) {
m_tabDialog = new TabDialog(this);
m_tabDialog->setWindowFlags(Qt::Dialog |
Qt::WindowCloseButtonHint |
Qt::WindowSystemMenuHint);
QObject::connect(&m_tabDialogTimer, &QTimer::timeout,
m_tabDialog, &QObject::deleteLater);
QObject::connect(m_tabDialog, &QDialog::finished, [&this](int){
// the dialog gets deleted 120 seconds after last use
m_tabDialogTimer.start(120);
});
}
m_tabDialogTimer.stop(); // reset pending timeout, if any
m_tabDialog->show();
}

How to ensure, the entire area in QScrollArea was displayed?

I'm displaying some information to the user in QScrollArea.
The user should have seen all contents, before she can proceed (at least the content should have been scrolled to the end)
How could I detect this in an easily?
Is the reimplementing of virtual void scrollContentsBy (int dx,int dy) the only way?
EDIT
I was able to solve it, but had to use some workarounds:
Scroll-action value sent by the signal actionTriggered(int) had never the value QAbstractSlider::SliderToMaximum (Qt4.8, Windows 7). So I've checked manually, if the slider value is close to maximum.
Even if scroll-bar widget was dragged by mouse till the bottom, the value of the scroll-bar is never the maximum. Only if the scroll-bar widget is moved by any other event such as arrow-down or mouse wheel, the value may become maximum. I've work-arounded it with recheckPosition()
I hope, there are better solutions.
void NegativeConfirmation::recheckPosition()
{
processScrollAction(1);
}
void NegativeConfirmation::processScrollAction( int evt)
{
if ( evt == QAbstractSlider::SliderToMaximum) // Have not managed to receive this action
{
ui->bConfirm->setEnabled(true);
}
//Another approach
QWidget * sw = ui->scrollArea->widget();
if ( sw ) //any content at all ?
{
QScrollBar * sb = ui->scrollArea->verticalScrollBar();
if ( sb )
{
int sbv = sb->value();
int sbm = sb->maximum()-10;
if ( sbm>0 && sbv >= sbm )
{
ui->bConfirm->setEnabled(true);
}
else
{
QTimer::singleShot(1000, this, SLOT(recheckPosition()));
}
}
}
}
QScrollArea inherits QAbstractSlider which provides this signal: -
void QAbstractSlider::actionTriggered(int action)
Where action can be QAbstractSlider::SliderToMaximum.
I expect you can connect to the this signal and test when the action is QAbstractSlider::SliderToMaximum, representing that the user has scrolled to the bottom.

How to make a Qt dialog read-only?

How to make a QT dialog read-only? Any general way to implement it easily? For example
(1) set all its containing widgets disable. (how to implement it?)
(2) Intercept edit events like key pressed, mouse pressed but how not to intercept the one to close the dialog?
I think this feature should be very helpful.
Disabling the widgets can be done similar to the following:
void myDialog::disableWidgets()
{
QList<QWidget *> widgets = this->findChildren<QWidget *>();
foreach(QWidget* widget, widgets)
{
widget->setEnabled(false);
}
}
To intercept events, QDialog includes the function installEventFilter(QObject*).
This allows you to use a separate object to receive all events passed to the dialog. You can then choose to handle the event in the object, or pass it on to the dialog itself by calling the base class QObject::eventFilter
class MyEventHandler : public QObject
{
Q_OBJECT
protected:
bool MyEventHandler::eventFilter(QObject *obj, QEvent *event)
{
// handle key press events
if (event->type() == QEvent::KeyPress)
{
// Do something
// ...
return true; // event handled by the class
}
else
{ // ignore this event and pass it to the dialog as usual
return QObject::eventFilter(obj, event);
}
}
return false;
};
QDialog* dlg = new QDialog;
MyEventHandler evtHandler = new MyEventHandler;
dlg->installEventFilter(evtHandler);
Read-only is a strange term to apply to a dialog. Disabling all widgets as above does the trick. If you only wanted to make the input part of a QInputDialog read-only (while leaving scrollbars, buttons, etc. enabled), you could adapt that code as below:
QInputDialog dialog(this);
dialog.setOptions(QInputDialog::UsePlainTextEditForTextInput);
dialog.setWindowTitle("Title");
dialog.setLabelText("Label");
dialog.setTextValue("1\n2\n3\n");
QList<QWidget *> widgets = dialog.findChildren<QWidget *>();
foreach(QWidget* widget, widgets) {
if (strcmp(widget->metaObject()->className(),"QPlainTextEdit")==0) {
QPlainTextEdit *t = static_cast<QPlainTextEdit*>(widget);
t->setReadOnly(true);
}
}
dialog.exec();

Accepting drops on a QGraphicsScene

I'm trying to implement drag'n'drop for a QGraphicsScene. Here are the events I've overloaded:
void TargetScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) {
bool acceptDrag = false;
const QMimeData* mime = event->mimeData();
// Is an image present?
if (mime->hasImage()) {
QImage img = qvariant_cast<QImage>(mime->imageData());
dragPix = QPixmap::fromImage(img);
acceptDrag = !dragPix.isNull();
}
event->setAccepted(acceptDrag);
}
void TargetScene::dropEvent(QGraphicsSceneDragDropEvent *event) {
// Add dragged pixmap to scene
QGraphicsPixmapItem* newPix = this->addPixmap(dragPix);
newPix->setPos(event->pos().x(), event->pos().y());
}
The scene still won't accept drops. I'm guessing that's because I can't do setAcceptDrops(true) on my QGraphicsScene.
How do I accept drops on a graphics scene?
The trick here is to ALSO accept the event in the QGraphicsScene::dragMoveEvent()!
The reason is the DEFAULT implementation which ignores drag and drop events if there is no item under the mouse!
Also refer to: http://www.qtcentre.org/threads/8022-QGraphicsScene-doesn-t-accept-Drops
Cheers

Resources