Another time source for QML animations? - qt

I am thinking about building a gstreamer plugin that renders animated QML graphics for later overlaying onto playing video. So far the idea to use QML looks very promising to me, except one problem. I need to be able to seek within the video, and the animations must also rewind and jump to the required point in time. Now that I read QML docs, I see that all the animations are typically time bound.
So my question is: is it possible to bind QML animations to some "time source", other than the real world time, which may not be monotonous (in fact, it can be manipulated by the application). Or, more generically, can I bind QML animation to a numeric value X, so that when it changes, my animation progresses, and there is a strict relation between X and animation state. I hope you get the idea.

This answer is suitable only for NumberAnimation object. Probably similar approach can be used to replace other Animation objects as well.
As already stated by ddriver there is no other way than work around.
Here is my solution to the problem. It may seem complicated but I can assure it is easy to use. At the end of this answer I put a link to the source code of example project using this code. You can try it.
Add these files to your project:
easingvalueforprogress.h
#ifndef EASINGVALUEFORPROGRESS_H
#define EASINGVALUEFORPROGRESS_H
#include <QObject>
#include <QEasingCurve>
class EasingValueForProgress : public QObject
{
Q_OBJECT
public:
explicit EasingValueForProgress(QObject *parent = 0);
Q_INVOKABLE double getValue(int easingEnum, double progress){
QEasingCurve easing((QEasingCurve::Type)easingEnum);
return easing.valueForProgress(progress);
}
signals:
public slots:
};
#endif // EASINGVALUEFORPROGRESS_H
easingvalueforprogress.cpp
#include "easingvalueforprogress.h"
EasingValueForProgress::EasingValueForProgress(QObject *parent) : QObject(parent)
{
}
XValueAnimation.qml
import QtQuick 2.0
Item {
id: xValueAnimator
property Item target
property string targetProperty
property double from
property double to
property int easing: Easing.Linear
property double xValue
onXValueChanged: {
if (target.hasOwnProperty(targetProperty)) {
target[targetProperty] = calculateCurrentValue(
from, to, easing, xValue);
}
else
console.error("XValueAnimator: target:", target,
"does not have property", targetProperty)
}
function calculateCurrentValue(
defaultFrom, defaultTo, animationEasing, xValue) {
return defaultFrom + (defaultTo - defaultFrom)
* easingValueForProgress.getValue(animationEasing, xValue)
}
}
Add this to your main.cpp:
#include <QQmlContext>
#include "easingvalueforprogress.h"
EasingValueForProgress easingValueForProgress;
engine.rootContext()->setContextProperty(
"easingValueForProgress", &easingValueForProgress);
Now you can use it like this (instead of NumberAnimation object):
XValueAnimator {
target: object_you_want_to_affect // for example id of the object
targetProperty: "property_to_affect" // for example "x"
from: 100
to: 500
easing: Easing.OutQuad // omit to use Easing.Linear
xValue: myXValue // your property holding values from 0 to 1
}
Here I provided working example project. Feel free to download and test it.

Related

How to access QTextEdit "layout" to add "status bar"?

This a modified question I have posted on this forum.
It is not a repost, for two reasons - I cannot edit the other post and I am trying to
resolve this issue from another angle. Besides the other post got derailed by people who mean well but did not really read the post.
I have a working C++ code which is using (QT) QTextEdit class to collect and process text data.
The class - as its name suggest - was designed to collect and analyze text.
The text is displayed in "view " area.
My task is to select ONE word of text and drag it to another GUI object.
I like to put the text being dragged into QT standard "status bar".
Normal QT GUI widgets are designed using QTDesigner. Integral part of such design process
is "layout".
As it stands , QTextEdit DOES NOT HAVE / USE "layout" or use it but it is NOT visible / accessible when QTextEdit is implemented - there is no need for it.
In order to add "status bar" I need to MODIFY the view to hold the current text editing
"layout" and add "status bar " layout.
I am unable to figure out how to get access to the QTextEdit class GUI layout.
I am asking for help to accomplish that- how to add "status bar" to EXISTING QTextEdit.
Please read the post carefully _ I need help with how to add "status bar" to EXISTING QTextEdit.
I do have an option to replace the QTextEdit with basic "widget" class but it "breaks " the working code and I rather not do that.
I did look into setting multiple inheritance - Qwidget and QTextEdit but did not work.
PLEASE Mr Higgins , editing my post for proper English grammar and composition DOES NOT solve the problem. So , please - don't.
I'm not entirely sure but your question seems to imply that inheriting from QTextEdit would be acceptable. If that's the case then you can probably make use of the fact that QTextEdit itself inherits QAbstractScrollArea and use the viewport margins to create an area in which to show a status bar of some sort.
Consider the following code...
#include <QApplication>
#include <QScrollBar>
#include <QStatusBar>
#include <QTextEdit>
namespace {
class text_edit: public QTextEdit {
using super = QTextEdit;
public:
explicit text_edit (QWidget *parent = nullptr)
: super(parent)
, m_status(this)
{
m_status.setStyleSheet("background-color: gray;");
m_status.showMessage("Status text goes here...");
show_status(true);
horizontalScrollBar()->installEventFilter(this);
verticalScrollBar()->installEventFilter(this);
setLineWrapMode(QTextEdit::NoWrap);
}
protected:
virtual bool eventFilter (QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::Show || event->type() == QEvent::Hide)
update_status_geometry();
return super::eventFilter(obj, event);
}
virtual void resizeEvent (QResizeEvent *event) override
{
super::resizeEvent(event);
update_status_geometry();
}
private:
void show_status (bool on)
{
if (on) {
setViewportMargins(0, 0, 0, m_status.height());
m_status.show();
} else {
setViewportMargins(0, 0, 0, 0);
m_status.hide();
}
}
void update_status_geometry ()
{
/*
* Calculate initial geometry of the QStatusBar based on its size hint.
*/
auto s = m_status.sizeHint();
s.rwidth() = width();
QRect geom(QPoint(0, 0), s);
geom.moveTop(height() - s.height());
/*
* Adjust the status bar geometry to allow for the scroll bars.
*/
if (horizontalScrollBar()->isVisible())
geom.moveTop(geom.top() - horizontalScrollBar()->height());
if (verticalScrollBar()->isVisible())
geom.setRight(geom.right() - verticalScrollBar()->width());
m_status.setGeometry(geom);
}
QStatusBar m_status;
};
}
int
main (int argc, char **argv)
{
QApplication app(argc, argv);
text_edit te;
te.show();
return app.exec();
}
Running the code above results in the following widget...

Would that be possible to build a particular customized QPushButton?

I was trying to understand if there is a way to build a particular customized QPushButton?
What I am trying to achieve is the following layout and appearance:
The button is shown below, notice the red line (which meas that the button is not clicked). I am not sure how to achieve the red line. I think it could be widget? or a QProgressbar, that when is clicked goes/loads up to green..I am not sure because I don't have enough experience and have been trying to build it. However this seems to be a bit tough:
And below how it should look like right after the click happened (note the green line):
Despite my efforts, I found some useful sources that I could use to get me started: for example this source was great to understand how to start. I studied the fact that in order to achieve that, the button need to be subclassed, and that is great because it lays some sort of route.
Below the code I used:
custombutton.h
#ifndef CUSTOMBUTTON_H
#define CUSTOMBUTTON_H
#include <QPushButton>
class CustomButton : public QPushButton
{
public:
CustomButton( const QString& text, QWidget* parent = 0 );
void writeText();
};
#endif // CUSTOMBUTTON_H
custombutton.cpp
#include "CustomButton.h"
#include "algorithm"
CustomButton::CustomButton( const QString& text, QWidget* parent )
: QPushButton( text, parent )
{
}
void CustomButton::writeText()
{
QString buttonText = text();
setText( buttonText );
}
main
#include <QApplication>
#include "CustomButton.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CustomButton w( "MyButton" );
w.show();
w.writeText();
a.exec();
return 0;
}
Another useful source I found is this one which also was useful.
The official documentation points to use the styles, but I am trying not to do that because I would like to solve the problem understanding what is the potential of subclassing with Qt.
Unless going in the style direction is the only possible way to solve this problem?
I would like to thank anyone in advance for sharing or pointing to a potential solution on how to do that.
You can set your button as checkable and then set a different icon for the 2 states.
In your case you'd have to set the red icon for the Normal mode and the green one for the Selected mode
Here's an example:
https://www.dropbox.com/s/x40byuyu2ph8m1y/CheckableButton.zip?dl=0
Here someone asked the same thing:
https://forum.qt.io/topic/72363/change-icon-of-pushbutton
Here you can read abouth the modes:
https://doc.qt.io/qt-5/qicon.html#Mode-enum
PS: Of course overriding QAbstractButton::paintEvent(QPaintEvent *event) is a viable option too

How can I get my caret to show up on first double-click action on a QGraphicsTextItem?

I am struggling to get a QGraphicsTextItem to work as a user friendly object.
Since it is very hard to move while being editable, I start it as not editable, and make it editable on double-click. Then turn editing off on losing focus.
My problem is, the caret does not show up on first edit.
I have tried getting the position based on mouse position (as in this question that was trying to solve a different problem), or calling the QGraphicsTextItem::mouseDoubleClickEvent(event);
No matter what I try, the caret is invisible on first action - until I start typing (or if I focus out and back in) - even though it is at the correct location.
After typing, or unselecting and reselecting , the caret shows up in normal location every time.
I have tried to call the QTextCursor in the item constructor, setting its position at 0, made no difference.
What made a difference : one of the 2 situations (neither of which I can do though):
a) start item with Qt::TextEditorInteraction in constructor
b) start item with no moving/focus/selectable flags
I can't do either - because my default state of item must be movable, and that interferes with text editing (as explained at start).
I have tried to disable those flags during editing though... with no effect.
Here is a simple code to demonstrate the problem, I hope somebody can have an idea.
mytextitem.h
#ifndef TEXTITEM_H
#define TEXTITEM_H
#include <QGraphicsTextItem>
class MyTextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
MyTextItem();
protected:
virtual void focusOutEvent (QFocusEvent * event);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event);
};
#endif // TEXTITEM_H
mytextitem.cpp
#include "mytextitem.h"
#include <QTextCursor>
#include <QAbstractTextDocumentLayout>
#include <QGraphicsSceneMouseEvent>
#include <QFont>
MyTextItem::MyTextItem()
{
setHtml("ABCD");
setFont(QFont("Arial", 50));
setTextInteractionFlags(Qt::NoTextInteraction);
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
}
void MyTextItem::focusOutEvent(QFocusEvent *event)
{
Q_UNUSED(event);
setTextInteractionFlags(Qt::NoTextInteraction);
QTextCursor _cursor = textCursor();
_cursor.clearSelection();
setTextCursor(_cursor);
}
void MyTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
setTextInteractionFlags(Qt::TextEditorInteraction);
QGraphicsTextItem::mouseDoubleClickEvent(event); // or the version in linked question
}
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene s;
QGraphicsView view(&s);
s.setSceneRect(-20, -100, 800, 600);
view.show();
MyTextItem* t = new MyTextItem();
s.addItem(t);
return app.exec();
}
I have also considered editing text - not sure if that would work but I think it would affect the undo stack which I will have to deal with soon....
How can I get my caret to show up on first double-click action on the text item ?
(As a user, not seeing a caret would make me uncertain if I can type... even though it works... I would not have confidence in the object if I do not have feedback of my action. That's why I care about this problem.)
I can't explain it... after trying EVERYTHING to get the caret to show, the solution was so simple:
I had to change the order of flags being set, in constructor.
The QGraphicsTextItem flag must be set AFTER setting the QGraphicsItem flags.
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
setTextInteractionFlags(Qt::NoTextInteraction);

How to make a Qt widget invisible without changing the position of the other Qt widgets?

I've got a window full of QPushButtons and QLabels and various other fun QWidgets, all layed out dynamically using various QLayout objects... and what I'd like to do is occasionally make some of those widgets become invisible. That is, the invisible widgets would still take up their normal space in the window's layout, but they wouldn't be rendered: instead, the user would just see the window's background color in the widget's rectangle/area.
hide() and/or setVisible(false) won't do the trick because they cause the widget to be removed from the layout entirely, allowing other widgets to expand to take up the "newly available" space; an effect that I want to avoid.
I suppose I could make a subclass of every QWidget type that override paintEvent() (and mousePressEvent() and etc) to be a no-op (when appropriate), but I'd prefer a solution that doesn't require me to create three dozen different QWidget subclasses.
This problem was solved in Qt 5.2. The cute solution is:
QSizePolicy sp_retain = widget->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
widget->setSizePolicy(sp_retain);
http://doc.qt.io/qt-5/qsizepolicy.html#setRetainSizeWhenHidden
The only decent way I know of is to attach an event filter to the widget, and filter out repaint events. It will work no matter how complex the widget is - it can have child widgets.
Below is a complete stand-alone example. It comes with some caveats, though, and would need further development to make it complete. Only the paint event is overridden, thus you can still interact with the widget, you just won't see any effects.
Mouse clicks, mouse enter/leave events, focus events, etc. will still get to the widget. If the widget depends on certain things being done upon an a repaint, perhaps due to an update() triggered upon those events, there may be trouble.
At a minimum you'd need a case statement to block more events -- say mouse move and click events. Handling focus is a concern: you'd need to move focus over to the next widget in the chain should the widget be hidden while it's focused, and whenever it'd reacquire focus.
The mouse tracking poses some concerns too, you'd want to pretend that the widget lost mouse tracking if it was tracking before. Properly emulating this would require some research, I don't know off the top of my head what is the exact mouse tracking event protocol that Qt presents to the widgets.
//main.cpp
#include <QEvent>
#include <QPaintEvent>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QGridLayout>
#include <QDialogButtonBox>
#include <QApplication>
class Hider : public QObject
{
Q_OBJECT
public:
Hider(QObject * parent = 0) : QObject(parent) {}
bool eventFilter(QObject *, QEvent * ev) {
return ev->type() == QEvent::Paint;
}
void hide(QWidget * w) {
w->installEventFilter(this);
w->update();
}
void unhide(QWidget * w) {
w->removeEventFilter(this);
w->update();
}
Q_SLOT void hideWidget()
{
QObject * s = sender();
if (s->isWidgetType()) { hide(qobject_cast<QWidget*>(s)); }
}
};
class Window : public QWidget
{
Q_OBJECT
Hider m_hider;
QDialogButtonBox m_buttons;
QWidget * m_widget;
Q_SLOT void on_hide_clicked() { m_hider.hide(m_widget); }
Q_SLOT void on_show_clicked() { m_hider.unhide(m_widget); }
public:
Window() {
QGridLayout * lt = new QGridLayout(this);
lt->addWidget(new QLabel("label1"), 0, 0);
lt->addWidget(m_widget = new QLabel("hiding label2"), 0, 1);
lt->addWidget(new QLabel("label3"), 0, 2);
lt->addWidget(&m_buttons, 1, 0, 1, 3);
QWidget * b;
b = m_buttons.addButton("&Hide", QDialogButtonBox::ActionRole);
b->setObjectName("hide");
b = m_buttons.addButton("&Show", QDialogButtonBox::ActionRole);
b->setObjectName("show");
b = m_buttons.addButton("Hide &Self", QDialogButtonBox::ActionRole);
connect(b, SIGNAL(clicked()), &m_hider, SLOT(hideWidget()));
QMetaObject::connectSlotsByName(this);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
#include "main.moc"
You can use a QStackedWidget. Put your button on the first page, a blank QWidget on the second, and change the page index to make your button vanish while retaining its original space.
I've 3 solutions in my mind:
1) Subclass your QWidget and use a special/own setVisible() replacement method witch turns on/off the painting of the widget (if the widget should be invisible simply ignore the painting with an overridden paintEvent() method). This is a dirty solution, don't use it if you can do it other ways.
2) Use a QSpacerItem as a placeholder and set it's visibility to the opposite of the QWidget you want to hide but preserve it's position+size in the layout.
3) You can use a special container widget (inherit from QWidget) which gets/synchronizes it's size based on it's child/children widgets' size.
I had a similar problem and I ended up putting a spacer next to my control with a size of 0 in the dimension I cared about and an Expanding sizeType. Then I marked the control itself with an Expanding sizeType and set its stretch to 1. That way, when it's visible it takes priority over the spacer, but when it's invisible the spacer expands to fill the space normally occupied by the control.
May be QWidget::setWindowOpacity(0.0) is what you want? But this method doesn't work everywhere.
One option is to implement a new subclass of QWidgetItem that always returns false for QLayoutItem::isEmpty. I suspect that will work due to Qt's QLayout example subclass documentation:
We ignore QLayoutItem::isEmpty(); this means that the layout will treat hidden widgets as visible.
However, you may find that adding items to your layout is a little annoying that way. In particular, I'm not sure you can easily specify layouts in UI files if you were to do it that way.
Here's a PyQt version of the C++ Hider class from Kuba Ober's answer.
class Hider(QObject):
"""
Hides a widget by blocking its paint event. This is useful if a
widget is in a layout that you do not want to change when the
widget is hidden.
"""
def __init__(self, parent=None):
super(Hider, self).__init__(parent)
def eventFilter(self, obj, ev):
return ev.type() == QEvent.Paint
def hide(self, widget):
widget.installEventFilter(self)
widget.update()
def unhide(self, widget):
widget.removeEventFilter(self)
widget.update()
def hideWidget(self, sender):
if sender.isWidgetType():
self.hide(sender)
I believe you could use a QFrame as a wrapper. Although there might be a better idea.
Try void QWidget::erase (). It works on Qt 3.

How to find global position of text cursor?

I would like to execute a QMenu object at the position of text cursor in a QPlainTextEdit. My problem is that QTextCursor is only define by its position in the Text (index of the character).
How can I find global position of the QTextCursor? Should I use an other object than QTextCursor in order to find the position of the text cursor where I want to open my QMenu?
Thank you by advance.
I've never tried myself, but doesn't QPlainTextEdit::cursorRect() work? It should give you position of the cursor in viewport coordinates. You can then get the viewport using viewport() and map the local position to global using viewport()->mapToGlobal().
I have found similar query to your in some online forum and here's someone suggested the output as
Note: Reference from http://www.unix.com/unix-linux-applications/81388-read-position-mouse-cursor.html, Author of below posting is daggilli, registered user of UNIX online forums. Credit of below posting in its complete form goes to daggilli.
This is the complete code for a Qt application I threw together in about ten minutes (called crosshair) which displays the current mouse coordinates in a window. You might be able to pull enough out of it to be useful. This is Qt 3.1, but Qt 4 is not a great deal different. You will need the Qt development libraries, not just the runtimes. The code comprises two files, crosshair.h and crosshair.cpp.
crosshair.h:
Code:
#ifndef CROSSHAIR_H
#define CROSSHAIR_H
#include <qwidget.h>
#include <qstring.h>
#include <qlabel.h>
#include <qevent.h>
class Crosshair : public QLabel
{
Q_OBJECT
public:
Crosshair(QWidget *parent=0);
protected:
void mousePressEvent(QMouseEvent *);
private:
QTimer *timer;
private slots:
void timerfire();
};
#endif
crosshair.cpp:
Code:
#include <qapplication.h>
#include <qpushbutton.h>
#include <qtimer.h>
#include <qcursor.h>
#include <iostream>
#include "crosshair.h"
using namespace std;
int main(int argc,char **argv)
{
QApplication a(argc,argv);
Crosshair mousepos;
a.setMainWidget(&mousepos);
mousepos.show();
return a.exec();
}
Crosshair::Crosshair(QWidget *parent) : QLabel(parent)
{
setIndent(20);
resize(100,30);
move(1200,200);
setText("0,0");
timer=new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(timerfire()));
timer->start(50,false);
}
void Crosshair::mousePressEvent(QMouseEvent *)
{
qApp->quit();
}
void Crosshair::timerfire()
{
QPoint p=QCursor::pos();
this->setText(QString().sprintf("%d,%d",p.x(),p.y()));
}
To build this, put both files in a directory called crosshair. cd to that directory and type
Code:
qmake -project
qmake
make
This does nothing more complex than inherit from a QLabel, set a timer to run 20x a second, grab the current cursor coordinates and write them into the label's text. Clicking in the window closes it. I use it for fixing up alignment bugs in JavaScript when I'm laying out objects.
You could open a file in the Crosshair class's constructor to store your data, and use gettimeofday(2) to get a timestamp. Nothing says Qt has to run in GUI mode (you can tell it explicitly not to in the QApplication constructor).
Qt from Trolltech: http://doc.trolltech.com

Resources