Custom map overlays using Marble - c++

I'm trying to create custom overlays in Marble while following this tutorial. My code is identical to the one in the example.
Everything seems ok, but somehow the generated layer is editable and I can click it and change its size.
I'd like it to be just static on the background with no way of interacting with it.
There doesn't seem to be any obvious flag to set or function to override (so that I could just ignore all user events).
Any ideas?
Code as requested:
#include <QDebug>
#include <QFileInfo>
#include <QApplication>
#include <QImage>
#include <marble/MarbleWidget.h>
#include <marble/GeoDataDocument.h>
#include <marble/GeoDataGroundOverlay.h>
#include <marble/GeoDataTreeModel.h>
#include <marble/MarbleModel.h>
using namespace Marble;
int main(int argc, char** argv) {
QApplication app(argc,argv);
QFileInfo inputFile( app.arguments().last() );
if ( app.arguments().size() < 2 || !inputFile.exists() ) {
qWarning() << "Usage: " << app.arguments().first() << "file.png";
return 1;
}
// Create a Marble QWidget without a parent
MarbleWidget *mapWidget = new MarbleWidget();
// Load the Satellite map
mapWidget->setMapThemeId( "earth/bluemarble/bluemarble.dgml" );
// Create a bounding box from the given corner points
GeoDataLatLonBox box( 55, 48, 14.5, 6, GeoDataCoordinates::Degree );
box.setRotation( 0, GeoDataCoordinates::Degree );
// Create an overlay and assign the image to render and its bounding box to it
GeoDataGroundOverlay *overlay = new GeoDataGroundOverlay;
overlay->setLatLonBox( box );
overlay->setIcon( QImage( inputFile.absoluteFilePath() ) );
// Create a document as a container for the overlay
GeoDataDocument *document = new GeoDataDocument();
document->append( overlay );
// Add the document to MarbleWidget's tree model
mapWidget->model()->treeModel()->addDocument( document );
mapWidget->show();
return app.exec();
}

Update:
You can programatically enable/disable plugins using RenderPlugin and setVisible:
QList<RenderPlugin *> renderPluginList = marbleWidget->renderPlugins();
for (RenderPlugin *renderPlugin : renderPluginList) {
if (std::find(plugin_list.begin(), plugin_list.end(), renderPlugin->nameId()) != plugin_list.end())
{
renderPlugin->setVisible(true);
}
else
{
renderPlugin->setVisible(false);
}
}
Where plugin_list is a std::vector<QString> of plugin nameId()s.
To disable just the Annotation plugin, you could use:
QList<RenderPlugin *> renderPluginList = mapWidget->renderPlugins();
for (RenderPlugin *renderPlugin : renderPluginList) {
if (renderPlugin->nameId() == "annotation")
{
renderPlugin->setVisible(false);
}
}
In case you are still experiencing this issue, one thing to check is whether you have the AnnotationPlugin (.dll if on Windows) in the plugins/ directory. This plugin allows for moving and resizing various features on the MarbleWidget map.

Related

[Qt][C++] setMinimumWidth and setMaximumWidth not working on label

I have label created and want tooltip over it, I want to set tooltip's maximum and minimum width but somehow its not working.
I am not expert to QT, not able to figure out the reason.
Code:
#include "widget.h"
#include <QApplication>
#include <QListWidget>
#include <QListWidgetItem>
#include <QLabel>
#include <QHBoxLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListWidget listWidget;
listWidget.setContentsMargins(QMargins(0, 0, 0, 0));
for (int i = 0; i < 5; ++i)
{
QListWidgetItem* item = new QListWidgetItem();
auto *itemWidget = new QWidget();
auto *textLabel = new QLabel(QString("Item %1").arg(i + 1), itemWidget);
textLabel->setMinimumWidth(100); //Not working whatever value I set
textLabel->setMaximumWidth(400); //Not working whatever value I set
textLabel->setToolTip("<p>This is the looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooonggggggggggggggg name </p>");
listWidget.addItem(item);
listWidget.setItemWidget(item, itemWidget);
}
listWidget.show();
return a.exec();
}
Tooltip:
can someone please help.
You cant directly set max and min on tooltip , hence you should indrectly do that(for this usecase):
static const QString FORMATTER = "<p>%1</p>";
QString tooltip =
"This is the "
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooonggggggggggggggg name.";
static constexpr auto maximum = 10;
textLabel->setToolTip(FORMATTER.arg(tooltip.mid(0, maximum)));
Update:
if you want exactly have your widget with any properties for tooltip , you can override your events and show the ToolTipWidget that reimplemented.
i.e: Qt WIdget inside ToolTip
textLabel->setMinimumWidth(100);
textLabel->setMaximumWidth(400);
This will set the min/max width of the label itself not the tooltip.
If you want to customize tooltip behavior, you'll have to override the event() function to catch QEvent::ToolTip (and probably QEvent::ToolTipChange) and draw it yourself using QToolTip::showText()
please use :textLabel->setToolTip

gtkmm : Drawing text with cairo

I want to draw a simple text with Cairo in an app using Gtkmm. I want to give the font style (it can be Pango::FontDescription or Pango::Context and so on ...) directly to draw text with Cairo when the Gtk::FontButton is clicked (in other words when the signal_font_set signal is issued). In the example below, I have a Gtk::HeaderBar that contains a Gtk::FontButton that sends a Glib::RefPtr<<Pango::Context>> to the drawing class when clicked. I want something like this:
MyWindow.cpp:
#include <iostream>
#include "MyWindow.h"
MyWindow::MyWindow() {
set_default_size(1000, 1000);
set_position(Gtk::WIN_POS_CENTER);
header.set_show_close_button(true);
header.pack_start(fontButton);;
set_titlebar(header);
fontButton.signal_font_set().connect([&] {
drawingArea.select_font(fontButton.get_pango_context());
});
scrolledWindow.add(drawingArea);
add(scrolledWindow);
show_all();
}
MyDrawing.h:
#ifndef DRAWING_H
#define DRAWING_H
#include <gtkmm.h>
#include <cairo/cairo.h>
class MyDrawing : public Gtk::Layout
{
public:
MyDrawing();
~MyDrawing() override;
void select_font(Glib::RefPtr<Pango::Context> p_pangoContext);
private:
bool draw_text(const Cairo::RefPtr<::Cairo::Context>& p_context);
Glib::RefPtr<Pango::Context> m_pangoContext;
};
#endif // DRAWING_H
and MyDrawing.cpp:
#include <iostream>
#include <utility>
#include "MyDrawing.h"
#include <cairomm/context.h>
#include <cairomm/surface.h>
MyDrawing::MyDrawing() {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_text));
}
MyDrawing::~MyDrawing() = default;
bool MyDrawing::draw_text(const Cairo::RefPtr<::Cairo::Context> &p_context) {
auto layout = create_pango_layout("hello ");
if(m_pangoContext) {
layout->set_font_description(m_pangoContext->get_font_description());
}
else {
Pango::FontDescription fontDescription;
layout->set_font_description(fontDescription);
}
p_context->save();
p_context->set_font_size(30);
p_context->set_source_rgb(0.1, 0.1, 0.1);
p_context->move_to(40, 40);
layout->show_in_cairo_context(p_context);
p_context->restore();
return true;
}
void MyDrawing::select_font(Glib::RefPtr<Pango::Context> p_pangoContext) {
this->m_pangoContext = std::move(p_pangoContext);
queue_draw();
}
when I set up Pango::FontDescription manually like :
Pango::FontDescription fontDescription;
fontDescription.set_weight(Pango::WEIGHT_BOLD);
fontDescription.set_style(Pango::STYLE_ITALIC);
layout->set_font_description(fontDescription);
it works:
Here is a way that works: instead of using a Pango::Context, you can get the font name from Gtk::FontButton::get_font_name and then create a Pango::FontDescription from it. Here is a minimal example to show this:
#include <iostream>
#include <gtkmm.h>
class MyDrawing : public Gtk::Layout
{
public:
MyDrawing();
void set_font(const std::string& p_selectedFont);
private:
bool draw_text(const Cairo::RefPtr<::Cairo::Context>& p_context);
std::string m_selectedFont;
Glib::RefPtr<Pango::Layout> m_pangoLayout;
};
MyDrawing::MyDrawing()
{
signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_text));
m_pangoLayout = Pango::Layout::create(get_pango_context());
m_pangoLayout->set_text(
"This text's appearance should change\n"
"when font description changes."
);
}
void MyDrawing::set_font(const std::string& p_selectedFont)
{
// Record the font selected by the user (as a string... Ugh!):
m_selectedFont = p_selectedFont;
std::cout << "Selected font: " << m_selectedFont << std::endl;
// Request a redraw:
queue_draw();
}
bool MyDrawing::draw_text(const Cairo::RefPtr<::Cairo::Context>& p_context)
{
// Create a font description from what was selected by the user earlier,
// and set it:
const Pango::FontDescription description{m_selectedFont};
m_pangoLayout->set_font_description(description);
p_context->save();
p_context->set_font_size(30);
p_context->set_source_rgb(0.1, 0.1, 0.1);
p_context->move_to(40, 40);
m_pangoLayout->show_in_cairo_context(p_context);
p_context->restore();
return true;
}
class MyWindow : public Gtk::ApplicationWindow
{
public:
MyWindow();
private:
Gtk::HeaderBar m_headerBar;
Gtk::FontButton m_fontButton;
MyDrawing m_drawingArea;
};
MyWindow::MyWindow()
{
m_headerBar.set_show_close_button(true);
m_headerBar.pack_start(m_fontButton);;
set_titlebar(m_headerBar);
m_fontButton.signal_font_set().connect(
[this]()
{
m_drawingArea.set_font(m_fontButton.get_font_name());
}
);
add(m_drawingArea);
show_all();
}
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
MyWindow window;
window.show_all();
return app->run(window);
}
I am not a Pango expert, so I don't know if there is a better way using your initial strategy. Furthermore, the string representing the font names seem to need to follow some convention. From the documentation of Pango::FontDescription::FontDescription(const Glib::ustring& font_name) (Gtkmm 3.24):
font_name must have the form "[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]",
where FAMILY-LIST is a comma separated list of families optionally
terminated by a comma, STYLE_OPTIONS is a whitespace separated list of
words where each WORD describes one of style, variant, weight, or
stretch, and SIZE is an decimal number (size in points). Any one of
the options may be absent. If FAMILY-LIST is absent, then the
family_name field of the resulting font description will be
initialized to 0. If STYLE-OPTIONS is missing, then all style options
will be set to the default values. If SIZE is missing, the size in the
resulting font description will be set to 0.
so you may have to do some testing of your own to make sure this works for all fonts. In my case, all 10-15 fonts I have tested seemed to work fine.

How to make GtkScrolledWindow work together with GtkFixed inside GtkOverlay

The basic problem is this:
I have a bunch of widgets that the user can move around. This is achieved by using a GtkOverlay as the outer container, and inside this, is a bunch of GtkFixed, each containing the corresponding widget:
GtkOverlay
GtkFixed
Widget A
GtkFixed
Widget B
...
The reason for the outer GtkOverlay is that GtkFixed lacks support for z-index. Now each GtkFixed covers the entire area. Thus, to make it possible to activate a widget that is currently below, the pass-through property has to be set on each child of the GtkOverlay. The system works for the leaf widgets GtkScale and GtkDrawingArea. However, If I put something inside a GtkScrolledWindow in GTK assumes that the GtkScrolledWindow is located in the upper left corner of the GtkFixed, which in general is wrong. Unsetting the pass-through property solves the problem, but then other widgets become inaccessible. Is there any workaround for this.
I think the proper solution would be to have each child of the GtkOverlay not to occupy the whole area. Then pass-through would not be needed, but I think it is not possible to do this with GTK (or maybe you could if you write your own layout container, which behaves like a web page with free floating layers.
Minimal non-working example:
//# {
//# "targets":[{"name":"minimal-nonworking-example-so-64090741","type":"application", "pkgconfig_libs":["gtk+-3.0"]}]
//# }
#include <gtk/gtk.h>
#include <string>
#include <vector>
#include <algorithm>
// To be able to feed the list box
template<class Callback>
void readlines(FILE* src, Callback&& cb)
{
std::string buffer;
while(true)
{
auto ch_in = getc(src);
if(ch_in == -1)
{
if(buffer.size() != 0)
{
cb(buffer);
}
return;
}
if(ch_in=='\n')
{
cb(buffer);
buffer.clear();
}
else
{
buffer+=ch_in;
}
}
}
int main(int argc, char** argv)
{
gtk_init(&argc, &argv);
auto mainwin = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_window_set_title(mainwin, "ListBoxTest");
gtk_window_set_default_size(mainwin, 800, 500);
auto overlay = GTK_OVERLAY(gtk_overlay_new());
gtk_container_add(GTK_CONTAINER(mainwin), GTK_WIDGET(overlay));
gtk_widget_set_size_request(GTK_WIDGET(overlay), 500, 300);
auto frame = GTK_FRAME(gtk_frame_new(nullptr));
auto fixed = GTK_FIXED(gtk_fixed_new());
gtk_fixed_put(fixed, GTK_WIDGET(frame), 60, 60);
gtk_overlay_add_overlay(overlay, GTK_WIDGET(fixed));
// {
// Without this line, GtkScrolledWindow works as expected, but if there were more layers
// other layers would become inaccessible
gtk_overlay_set_overlay_pass_through(overlay, GTK_WIDGET(fixed), TRUE);
// }
gtk_overlay_reorder_overlay(overlay, GTK_WIDGET(fixed), -1);
auto scrolled_window = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(nullptr, nullptr));
gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(scrolled_window));
gtk_widget_set_size_request(GTK_WIDGET(frame), 128, 128);
auto listbox = GTK_LIST_BOX(gtk_list_box_new());
gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(listbox));
gtk_widget_show_all(GTK_WIDGET(mainwin));
readlines(stdin, [listbox](auto&& item) {
auto label = GTK_LABEL(gtk_label_new(item.c_str()));
gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START);
gtk_list_box_insert(listbox, GTK_WIDGET(label), -1);
});
gtk_widget_show_all(GTK_WIDGET(fixed));
gtk_main();
gtk_widget_destroy(GTK_WIDGET(mainwin));
return 0;
}

C++ / QML interactive combo box implementation

I have two combo boxes, the data for the second one being determined by that of the first one. The number of strings in the second combo box varies from 2 to 4. If I:
select a new string in the first combo box and
the last choice is selected
in the second combo box with a longer list than the previous list of
that box,
the currentString in the second combo box remains and overrides the correct text
For instance, if I select Scubapro in the first combo box (4 options in 2nd box) and Smart in the second combo box (the 4th option), then select any other choice in the first combo box again (< 4 options in 2nd box), the entry in the second combo box remains "Smart", which is inappropriate. The correct list is, however, loaded into the 2nd combo box. Inspection of the underlying stringlist also suggests that it contains the correct data. The problem appears to be the visual updating of the second combo box. The heart of the algorithm comes from Stackoverflow and is the generator called each time text in combo box 1 changes.
What can one do to rectify this?
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QQuickView>
#include <QQuickItem>
#include <QStringListModel>
#include <QQmlContext>
#include <QDebug>
QStringList dat1, dat2, dat3;
QStringList vendorList;
class Generator : public QObject
Q_OBJECT
QStringListModel * m_model;
public:
Generator(QStringListModel * model) : m_model(model) {}
Q_INVOKABLE void generate(const QVariant & val) {
m_model->removeRows(0,m_model->rowCount()); // This has no effect
if (QString::compare(val.toString(), QString::fromStdString("Mares"), Qt::CaseInsensitive) == 0) {
m_model->setStringList(dat1);
}
else {
if (QString::compare(val.toString(), QString::fromStdString("ScubaPro"), Qt::CaseInsensitive) == 0) {
m_model->setStringList(dat2);
}
else
m_model->setStringList(dat3);
}
};
int main(int argc, char *argv[])
{
QStringListModel model1, model2;
generator(&model2);
dat1 << "Puck" << "Nemo" << "Matrix";
dat2 << "Aladin" << "Meridian" << "Galilio" << "Smart";
dat3 << "D4" << "D6";
vendorList << "Mares" << "Scubapro" << "Suunto" << "Shearwater";
model1.setStringList(vendorList);
QGuiApplication app(argc, argv);
QQuickView view;
QQmlContext *ctxt = view.rootContext();
ctxt->setContextProperty("model1", &model1);
ctxt->setContextProperty("model2", &model2);
ctxt->setContextProperty("generator", &generator);
view.setSource(QUrl("qrc:main.qml"));
view.show();
return app.exec();
}
#include "main.moc"
Here is the QML:
import QtQuick 2.0
import QtQuick.Controls 1.0
Rectangle {
width: 400; height: 300
Text { text: "Vendor"; }
Text {
x: 200
text: "Product"; }
ComboBox {
id: box2
objectName: "productBox"
x:200; y:25; width: 180
model: model2
textRole: "display"
}
ComboBox {
y:25; width: 180
id: box1
model: model1
textRole: "display"
onCurrentTextChanged: {
generator.generate(currentText)
}
}
}
Any comments are highly appreciated.
The ComboBox item does not react to changes performed under the hood to the model.
There are a couple of solutions to work around it.
A possible one is to reassign the model to itself at the end of the signal handler, by using the statement:
model = model;
As from the documentation:
Changing the model after initialization will reset currentIndex to 0.
Otherwise, you can explicitly set currentIndex to your preferred value or, even better, to -1.
In fact, from the documentation we have that:
Setting currentIndex to -1 will reset the selection and clear the text label.

Fitting rows in QTableView, getting rid of extra space

I'm trying to generate a simple table (2 rows and 2 columns) and write it to a pdf file, using Qt 4.8.0.
So far, I generate the pdf but there is extra space at the bottom of the "printed" table:
I got the same problem with the right side of the table but I managed to get rid of it. But in this case I am clueless.
Here's the code I have now (all of this code is located in main.cpp):
Main
#include <QtGui/QApplication>
#include <QtCore/QDebug>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtGui/QPrinter>
#include <QtGui/QHeaderView>
#include <QtGui/QPainter>
#include <QtGui/QTableWidget>
#include <QtGui/QTableWidgetItem>
/**/
/* Here are the functions.
/**/
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QMap<QString,int> values;
values.insert("X",7);
values.insert("Y",13);
bool status = TableWidgetToPdf("FromWidget.pdf",values);
return a.exec();
}
TableWidgetToPdf
bool TableWidgetToPdf(const QString& title, const QMap<QString, int>& values) {
QTableWidget* table = GenerateTable(values);
QPrinter printer;
printer.setOutputFileName(title);
printer.setOutputFormat(QPrinter::PdfFormat);
QPainter painter(&printer);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
printer.setPaperSize(QPrinter::A4);
table->render(&painter);
painter.end();
printer.newPage();
delete table;
return true;
};
GenerateTable
QTableWidget* GenerateTable(const QMap<QString,int>& values) {
QTableWidget* table = new QTableWidget;
table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
table->setRowCount(2);
table->setColumnCount(2);
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
table->setShowGrid(false);
table->verticalHeader()->hide();
QStringList h_labels;
h_labels << "X" << "Y";
table->setHorizontalHeaderLabels(h_labels);
table->horizontalHeader()->setFont( QFont("Times", 10, QFont::Bold) );
table->horizontalHeader()->setStretchLastSection(true);
QTableWidgetItem* item00 = new QTableWidgetItem( QString::number(values["X"]));
item00->setTextAlignment(Qt::AlignCenter);
table->setItem(0,0, item00 );
QTableWidgetItem* item01 = new QTableWidgetItem( QString::number(values["Y"]) );
item01->setTextAlignment(Qt::AlignCenter);
table->setItem(0,1,item01);
table->setItem(1,0,new QTableWidgetItem("ABCD"));
return table;
};
NOTE:
Putting
table->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
table->verticalHeader()->setResizeMode(QHeaderView::Stretch);
in GenerateTable the space disappears, but the cells are resized and consume too much space than needed for their contents. I would like to avoid that if possible:
EDIT:
OK.
In the end I achieved what I wanted by getting rid of the QTableWidget. I had to create the table using html and feeding it to a QTextEditor. Isn't any way to achieve this with a QTableWidget?
Have you tried the flags for resize content?
Try the following code, I don't have access to Qt right now.
table->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
table->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
Hope that works!
I realise that this is an old post but it seems fairly often read.
I tried the same methods that you tried and none worked. Eventually I used a QTableView and added an extra method called by adding/removing rows.
void
TitleView::verticalResizeTableViewToContents()
{
auto count = m_model->rowCount(QModelIndex());
auto scrollBarHeight = horizontalScrollBar()->height();
auto horizontalHeaderHeight = horizontalHeader()->height();
auto rowTotalHeight = scrollBarHeight + (horizontalHeaderHeight * count);
setMinimumHeight(rowTotalHeight);
setMaximumHeight(rowTotalHeight);
}