QtMediaPlayer duration is always 0 - qt

I'm on ubuntu 20.04 using Qt 5.14.
QtMdeiaPlayer duration always returns 0.
Documentation states that call to SetMedia is not blocking so duration will be zero right after the call, but I connected a signal as stated in the documentation.
connect(player, &QMediaPlayer::durationChanged, this, [&](qint64 dur) {
qDebug() << "duration = " << dur;
});
This lambda is never called. Furthermore audio is playing normally and signal for positionChanged works as expected.
I tested several audio files of different formats and result is the same.
Any ideas why it could happen?
-- edit --
Minimal code to reproduce
// somewhere
static QMediaPlayer *player = new QMediaPlayer;
static void DurationChanged(quint64 duration) {
qDebug() << "durration " << duration;
}
static void PositionChaned(quint64 position) {
qDebug() << "position" << position << "duration" << player->duration();
}
// in main
auto path = QUrl::fromLocalFile(QFileInfo("sample.mp3").absoluteFilePath());
QObject::connect(player, &QMediaPlayer::durationChanged, DurationChanged);
QObject::connect(player, &QMediaPlayer::positionChanged, PositionChaned);
player->setMedia(path);
player->play();
// my output
// position 0 duration 0
// position 1032 duration 0
// position 2083 duration 0

I have used a ubuntu 20.04 docker and I get the duration of the audio:
#include <QtMultimedia>
static void DurationChanged(quint64 duration) {
qDebug() << "duration " << duration;
}
static void PositionChaned(quint64 position) {
qDebug() << "position" << position;
}
int main(int argc, char *argv[])
{
QGuiApplication a(argc, argv);
// in main
QDir dir_path = QCoreApplication::applicationDirPath();
auto path = dir_path.filePath("sample.mp3");
auto url = QUrl::fromLocalFile(path);
QMediaPlayer player;
QObject::connect(&player, &QMediaPlayer::durationChanged, DurationChanged);
QObject::connect(&player, &QMediaPlayer::positionChanged, PositionChaned);
player.setMedia(url);
player.play();
return a.exec();
}
Output:
position 0
position 0
position 9
duration 26000
position 987
position 1945
position 2883
position 3821
position 4789
position 5786
position 6784
position 7792
position 8790
position 9797
position 10775
position 11783
position 12780
position 13788
position 14786
position 15794
position 16791
position 17799
position 18797
position 19804
position 20782
position 21790
position 22788
position 23795
position 24793
position 25801
position 26000
For the audio plugin to work install the following packages:
sudo apt-get install -y --no-install-recommends \
libgstreamer1.0-0 \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav \
gstreamer1.0-doc \
gstreamer1.0-tools \
libpulse-mainloop-glib0 \
alsa-base \
alsa-utils \
pulseaudio

Related

QListView max number of items in view

I need to calculate max number of items in current view of QListView.
I wrote code like this:
void MyListView::resizeEvent(QResizeEvent *event)
{
QListView::resizeEvent(event);
QFontMetrics fm (this->font());
int fontHeight = fm.lineSpacing();
QRect cr = contentsRect();
int windowHeight = cr.bottom() - cr.top();
int maxItemsCount = windowHeight / fontHeight;
qDebug()<<"max items in view: "<< maxItemsCount;
}
but calculated max number of items is is incorrect.
E.g. in case of my window height and font height I get 32 max items in view when in fact current view has 28 items. Perhaps someone can suggest something, how to calculate it properly?
My idea is to use QListView::indexAt() (inherited from QAbstractView) to obtain the row index for
the top-left corner
the bottom-left corner
of the list view viewport and determining the number of visible items by difference of them.
To check this out, I made an MCVE testQListViewNumVisibleItems.cc:
// Qt header:
#include <QtWidgets>
class ListWidget: public QListWidget {
public:
ListWidget(QWidget *pQParent = nullptr): QListWidget(pQParent) { }
virtual ~ListWidget() = default;
ListWidget(const ListWidget&) = delete;
ListWidget& operator=(const ListWidget&) = delete;
int getNumVisibleItems() const
{
const QSize size = viewport()->size();
const QModelIndex qMIdx0 = indexAt(QPoint(0, 0));
const QModelIndex qMIdx1 = indexAt(QPoint(0, size.height() - 1));
//qDebug() << "qMIdx0:" << qMIdx0 << "qMIdx1:" << qMIdx1;
return qMIdx0.isValid()
? (qMIdx1.isValid() ? qMIdx1.row() : count()) - qMIdx0.row()
: 0;
}
};
const int MaxItems = 20;
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
ListWidget qLst;
qLst.resize(200, 200);
qLst.show();
// timer to populate list view
using namespace std::chrono_literals;
QTimer qTimer;
qTimer.setInterval(1000ms);
// install signal handlers
int n = 0;
QObject::connect(&qTimer, &QTimer::timeout,
[&]() {
qLst.addItem(QString("item %0").arg(++n));
qDebug() << "Visible items:" << qLst.getNumVisibleItems();
if (n >= MaxItems) qTimer.stop();
});
// runtime loop
qTimer.start();
return app.exec();
}
and a CMakeLists.txt:
project(QListViewNumVisibleItems)
cmake_minimum_required(VERSION 3.10.0)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt5Widgets CONFIG REQUIRED)
include_directories("${CMAKE_SOURCE_DIR}")
add_executable(testQListViewNumVisibleItems testQListViewNumVisibleItems.cc)
target_link_libraries(testQListViewNumVisibleItems Qt5::Widgets)
built and tested in VS2017 on Windows 10:
After having implemented what came in my mind, I googled a bit to possibly see other approaches. (I admit I should've done before.)
Thereby I found the following possible duplicate:
SO: Simple way to get all visible items in the QListView
The accepted answer doesn't contain much more than the hint for indexAt and a link to a Qt-FAQ article:
How can i get hold of all of the visible items in my QListView?
In order to get hold of the visible items in a QListView http://doc.qt.io/qt-5/qlistview.html, then you can iterate over them using indexAt() http://doc.qt.io/qt-5/qlistview.html#indexAt. You can get hold of the first visible item using indexAt(QPoint(0, 0)), then in order to get the index at the next position then use visualRect() http://doc.qt.io/qt-5/qlistview.html#visualRect to find out what your next call to itemAt() should be. This position would be:
visualRect.y() + visualRect.height() + 1 effectively.
See the following example for an illustration:
#include <QtGui>
QList <QModelIndex>myList;
class ListView : public QListView
{
Q_OBJECT
public:
ListView()
{
QStringListModel *myModel = new QStringListModel(this);
QStringList list;
list << "a" << "b" <<"c" <<"d" <<"e" <<"f" <<"g" <<"h" <<"i" <<"j" <<"k";
myModel->setStringList(list);
setModel(myModel);
QTimer::singleShot(3000, this, SLOT(test1()));
}
public slots:
void test1()
{
QModelIndex firstIndex = indexAt(QPoint(0, 0));
if (firstIndex.isValid()) {
myList << firstIndex;
} while (viewport()->rect().contains(QPoint(0, visualRect(firstIndex).y() + visualRect(firstIndex).height() + 1 ))) {
firstIndex = indexAt(QPoint(0, visualRect(firstIndex).y() + visualRect(firstIndex).height() + 1 ));
myList << firstIndex;
}
qDebug() << myList.count() << "are visible";
}
};
#include "main.moc"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
ListView window;
window.resize(100, 50);
window.show();
return app.exec();
}

QT: Generate a bar graph in a headless AMI Server. Cannot start X Display

I'm doing some processing on an Amazon Linux EC2 AWS server. When something goes wrong I need to send an email with a bar graph. I want to generate a bar graph myself, and I'm doing so by using QPainter and QImage. I'm developing an example application to simply generate the bar graph, right now. So what I do is I develop on my laptop and when I have something that works upload the code to the EC2 server and compile it there. Then run the resulting application in the server.
However, here is the hiccup. When I tried to draw text using QPainter, the program in my laptop crashes. This is because I use a QCoreApplication instead of QApplication in my main.cpp.
So I change to QApplication which fixes the problem. I upload the code to the server and compile it. Then when I try to run it, I get:
qt.qpa.screen: QXcbConnection: Could not connect to display
Could not connect to any X display.
This is, of course, because the server is headless.
My question is: is there something I can install in the server that would allow me to fully use QPainter's capabilities?
Edit:
My main.cpp
#include <QApplication>
#include "../../CommonClasses/DataAnalysis/BarGrapher/bargrapher.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Setup
qreal W = 1024;
qreal H = 768;
// A fictional data set;
QList<qreal> ds1;
ds1 << 50 << 500 << 368 << 198;
QStringList descs;
descs << "Item 1";
descs << "A somewhat long and complex description";
descs << "Item 2";
descs << "Item Another";
// The actual bar graph
BarGrapher bg;
bg.configureGraphText(W,H,descs,"Some sort of label","The title of this damn thing");
BarGrapher::DataSet ds;
ds << ds1;
QImage result = bg.generateGraph(ds);
qWarning() << "ERROR" << bg.getError();
result.save("mytest.png");
qWarning() << "DONE";
return a.exec();
}
My bargrapher.cpp
#include "bargrapher.h"
const qreal BarGrapher::PROYECTION_FACTOR = 0.707106;
const qreal BarGrapher::LEFT_MARGIN = 0.1;
const qreal BarGrapher::RIGHT_MARGIN = 0.04;
const qreal BarGrapher::TOP_MARGIN = 0.08;
BarGrapher::BarGrapher()
{
}
void BarGrapher::configureGraphText(qreal width, qreal height, const QStringList &xItems, const QString &ylabel, const QString &title){
graphTitle = title;
W = width;
H = height;
itemDescriptions = xItems;
yAxisText = ylabel;
}
QImage BarGrapher::generateGraph(const DataSet &dataSet){
QImage graph(W,H,QImage::Format_RGB888);
error = "";
// Making sure that the data is coherent.
if (dataSet.isEmpty()){
error = "No data was presented";
return graph;
}
if (dataSet.size() > 2){
error = "Cannot have more than two data sets, passed " + QString::number(dataSet.size());
return graph;
}
qint32 numItems = dataSet.first().size();
for (qint32 i = 0; i < dataSet.size(); i++){
if (numItems != dataSet.at(i).size()){
error = "All data sets must be the same size: " + QString::number(numItems) + " however data set " + QString::number(i) + " has " + QString::number(dataSet.at(i).size());
return graph;
}
}
if (!itemDescriptions.isEmpty()){
if (numItems != itemDescriptions.size()){
error = "The number of item descriptios (" + QString::number(itemDescriptions.size()) + ") must coincide with the numer of points in the data set (" + QString::number(numItems) + ")";
return graph;
}
}
else{
for (qint32 i = 0; i < numItems; i++){
itemDescriptions << QString::number(i);
}
}
// The font to be used
QFont textFont("Mono");
textFont.setPixelSize(10);
//QFont titleFont("Mono",16,QFont::Bold);
// Calculating margins to get the effective graph area.
qreal XAxisLength = (1.0 - LEFT_MARGIN - RIGHT_MARGIN)*W;
qreal DW = XAxisLength/(qreal)(numItems);
// Calculating the effective graph area due to the text items.
qreal Th = 0.1*H;
qreal GH = (1.0-TOP_MARGIN)*H - Th;
qreal xOffset = LEFT_MARGIN*W;
qreal yOffset = TOP_MARGIN*H;
// --------------- Commence drawing -----------------------
QPainter painter(&graph);
// The background
painter.setBrush(QBrush(QColor(226,226,226)));
painter.drawRect(0,0,W,H);
// Drawing the axis;
QPen pen;
pen.setColor(QColor(0,0,0));
pen.setWidth(2);
painter.setPen(pen);
painter.setBrush(QBrush(Qt::black));
painter.drawLine(xOffset,yOffset+GH,xOffset + XAxisLength,yOffset+GH);
painter.drawLine(xOffset,yOffset,xOffset,yOffset+GH);
// Drawing the X Axis text
painter.setFont(textFont);
for (qint32 i = 0; i < numItems; i++){
QRectF rectF(xOffset + i*DW,yOffset+GH,DW,Th);
painter.drawText(rectF,itemDescriptions.at(i));
}
return graph;
}

Reading from character device with Qt

I'm not very good at character devices, so I need your help. A have a char device(let's call it /dev/my_light) which is a light sensor. I have to read the data from this file and transform it to the brightness value and then pass it to the brightness manager that changes the brightness of my screen. The problem is that when I read the value for some period of time I get old values from the file.I assume there is a buffer(again not sure how character devices exactly work). Whereas when I use cat /dev/my_light I see new data! Is it possible to get rid off the buffer and read new values that were written to the file just right now. Here is my code in Qt:
void MySensor::updateMySensor()
{
Packet packet;
packet.startByte = 0;
packet.mantissa = 0;
packet.exp = 0;
d->device = ::open(d->path.toStdString().c_str(), O_RDONLY);
if (d->device == -1)
{
qDebug() << Q_FUNC_INFO << "can't open the sensor";
return;
}
ssize_t size = ::read(d->device, &packet, sizeof(packet));
close(d->device);
if (size == -1)
{
qDebug() << errno;
return;
}
packet.exp &= 0x0F;
float illumination = pow(2, packet.exp) * packet.mantissa * 0.045;
if(d->singleShot) emit lightSensorIsRunning(true);
emit illuminationRead(illumination);
}
The mySensor function is called every second. I tried to call it each 200 msec but it didn't help. The value of illumination stays old for about 7 seconds(!) whereas the value that I get from cat is new just immediately.
Thank you in advance!
I can't test with your specific device, however, I'm using the keyboard as a read only device.
The program attempts to connect to keyboard and read all keys pressed inside and outside the window. It's a broad solution you'll have to adapt to meet your demands.
Note that I'm opening the file with O_RDONLY | O_NONBLOCK which means open in read only mode and no wait for the event be triggered(some notifier needed to know when data is ready!) respectively.
You'll need super user privilege to run this example!
#include <QtCore>
#include <fcntl.h>
#include <linux/input.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
const char *device_name = "/dev/input/by-path/platform-i8042-serio-0-event-kbd";
int descriptor = open(device_name, O_RDONLY | O_NONBLOCK);
if (descriptor < 0)
{
qDebug() << "Error" << strerror(errno);
return a.exec();
}
QFile device;
if (!device.open(descriptor, QFile::ReadOnly))
{
qDebug() << "Error" << qPrintable(device.errorString());
return a.exec();
}
QSocketNotifier notifier(device.handle(), QSocketNotifier::Read);
QObject::connect(&notifier, &QSocketNotifier::activated, &notifier, [&](int socket){
Q_UNUSED(socket)
struct input_event ev;
QByteArray data = device.readAll();
qDebug() << "Event caught:"
<< "\n\nDATA SIZE" << data.size()
<< "\nSTRUCT COUNT" << data.size() / int(sizeof(input_event))
<< "\nSTRUCT SIZE" << sizeof(input_event);
qDebug() << ""; //New line
while (data.size() >= int(sizeof(input_event)))
{
memcpy(&ev, data.data(), sizeof(input_event));
data.remove(0, int(sizeof(input_event)));
qDebug() << "TYPE" << ev.type << "CODE" << ev.code << "VALUE" << ev.value << "TIME" << ev.time.tv_sec;
}
qDebug() << ""; //New line
});
return a.exec();
}

How to extract a set of points in a point cloud data using PCL?

I have a point cloud data, where by clicking a point, I want to extract points surrounding the clicked point within a radius. I want to also push the extracted points into a new cloud. Using Pointpickingevent, I am able to click one point and push it into the cloud. How do I extract a set of points, say points surrounding 0.02cm radius from the clicked point and push them into a new cloud?
Given a point cloud:
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud
A Kdtree is then generated to perform an efficient range search:
pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
kdtree.setInputCloud (cloud);
Then, given a point and a radius:
pcl::PointXYZ searchPoint(1,2,3);
float radius = 4;
You can get all the points that are at a distance radius from the point searchPoint:
std::vector<int> pointIdxRadiusSearch; //to store index of surrounding points
std::vector<float> pointRadiusSquaredDistance; // to store distance to surrounding points
if ( kdtree.radiusSearch (searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0 )
{
for (size_t i = 0; i < pointIdxRadiusSearch.size (); ++i)
std::cout << " " << cloud->points[ pointIdxRadiusSearch[i] ].x
<< " " << cloud->points[ pointIdxRadiusSearch[i] ].y
<< " " << cloud->points[ pointIdxRadiusSearch[i] ].z
<< " (squared distance: " << pointRadiusSquaredDistance[i] << ")" << std::endl;
}
You can print all the surrounding points and their distance to the searchPoint to check the code functional correctness.
Finally, create a cloud with the obtained points:
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);
for (size_t i = 0; i < pointIdxRadiusSearch.size (); ++i)
cloud_cluster->points.push_back(cloud->points[ pointIdxRadiusSearch[i] ]);
cloud_cluster->width = cloud_cluster->points.size ();
cloud_cluster->height = 1;
cloud_cluster->is_dense = true;
In order to be able to pick a point you can use PointPickingEvent similarly to this answer.
The Class declaration in your .h,
class PCLViewer : public QMainWindow
{
Q_OBJECT
public:
explicit PCLViewer (QWidget *parent = 0);
~PCLViewer ();
void pointPickCallback (const pcl::visualization::PointPickingEvent& event, void*);
public slots:
protected:
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
pcl::PointXYZ src_point_;
bool src_point_selected_;
private:
Ui::PCLViewer *ui;
};
In your .cpp,
PCLViewer::PCLViewer (QWidget *parent) :
QMainWindow (parent),
ui (new Ui::PCLViewer)
{
ui->setupUi (this);
[...]
viewer.reset (new pcl::visualization::PCLVisualizer ("viewer", false));
viewer->registerPointPickingCallback (&PCLViewer::pointPickCallback, *this);
[...]
}
and the additional function,
void
PCLViewer::pointPickCallback (const pcl::visualization::PointPickingEvent& event, void*)
{
// Check to see if we got a valid point. Early exit.
int idx = event.getPointIndex ();
if (idx == -1)
return;
// Get the point that was picked
event.getPoint (src_point_.x, src_point_.y, src_point_.z);
PCL_INFO ("Src Window: Clicked point %d with X:%f Y:%f Z:%f\n", idx, src_point_.x, src_point_.y, src_point_.z);
src_point_selected_ = true;
}
There is a more detailed example of the utilisation of it in the manual registration app:
pcl/apps/src/manual_registration/manual_registration.cpp
pcl/apps/include/pcl/apps/manual_registration.h

convert GIcon to QIcon

Is there a way to convert between these datatypes? I'm working with Qt but still need some of glib capabilities and I haven't found a way to do this. I need to get a list of the installed applications with GAppInfo and show it in a QListView and to do so I need to get the icon for those applications. Extracting it using g_app_info_get_icon returns a GIcon and what I need to work with is a QIcon in order to get it's QVariant.
GIcon does not provide the actual pixmap for the icon.
You need to load an actual pixmap by requesting icon of certain size.
#include <gio/gdesktopappinfo.h>
#include <gtk/gtk.h>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QIcon icon;
GAppInfo *appInfo = (GAppInfo *)g_desktop_app_info_new("vlc.desktop");
GIcon *gicon = g_app_info_get_icon(appInfo);
QList<int> sizes; sizes << 16 << 24 << 32 << 48 << 64;
foreach (int size, sizes) {
GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(), gicon, size, (GtkIconLookupFlags)0);
GdkPixbuf *pixbuf = gtk_icon_info_load_icon(iconInfo, NULL);
if (pixbuf == NULL)
continue;
QImage image(
gdk_pixbuf_get_pixels(pixbuf),
gdk_pixbuf_get_width(pixbuf),
gdk_pixbuf_get_height(pixbuf),
gdk_pixbuf_get_rowstride(pixbuf),
QImage::Format_ARGB32);
g_object_unref(pixbuf);
image.save("icon-" + QString::number(size) + ".png");
icon.addPixmap(QPixmap::fromImage(image));
}
g_object_unref(gicon);
return 0;
}

Resources