"Name: value" widget via GTK - c++

How to make a widget via GTK that looks like the following?
------------------
| Text1: | 1 |
|-----------+----|
| Text2: | 10 |
|-----------+----|
| | |
| | |
| | |
------------------
'Text1', 'Text2' - constant strings; '1', '10' - dynamically changed values.
What Gtk::Widget's should I use for that?

Make Gtk::Grid with labels, align them, set column spacing and column homogeneous.
#include <gtkmm.h>
class window : public Gtk::Window {
protected:
Gtk::Box box;
Gtk::Grid grid;
Gtk::Button button;
Gtk::Label name1, name2, value1, value2;
int n_click = 0;
public:
window();
~window() = default;
void on_click() {
value1.set_label("1");
value2.set_label(n_click % 2 ? "1" : "10");
n_click++;
queue_draw();
}
};
window::window() : button("Update"),
name1("Text:"),
name2("Text2:")
{
add(box);
box.pack_start(button);
button.signal_clicked().connect(sigc::mem_fun(*this, &window::on_click));
box.pack_end(grid, Gtk::PACK_SHRINK);
grid.attach(name1, 0, 0, 2, 1);
grid.attach(value1, 2, 0, 1, 1);
grid.attach(name2, 0, 1, 2, 1);
grid.attach(value2, 2, 1, 1, 1);
name1.set_halign(Gtk::ALIGN_START);
name2.set_halign(Gtk::ALIGN_START);
value1.set_halign(Gtk::ALIGN_START);
value2.set_halign(Gtk::ALIGN_START);
grid.set_column_spacing(10);
grid.set_column_homogeneous(true);
show_all();
}
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "");
window win;
return app->run(win);
}

Related

How to change the label positioning in a Fl_Box?

I want to customize the label position inside a Fl_Box. Looking at the documentation I saw the draw_label() function here: this is a protected member of Fl_Widget, hence I derived a custom class for Fl_Box. The code is below.
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
class mBox: public Fl_Box{
public:
mBox(int X, int Y, int W, int H, const char* l=0): Fl_Box(X,Y,W,H,l){};
void drawLabel(){
label("New");
draw_label(x(),y(),100,25);
redraw_label();
};
};
void action(Fl_Widget* w, void* data){
mBox* B = (mBox*) data;
B -> drawLabel(); }
int main(int argc, char **argv) {
Fl_Window* G_win;
G_win = new Fl_Window(180,100,"The font test");
mBox* A = new mBox(10,10,110,50,"The font TEST.");
A -> box(FL_UP_BOX);
Fl_Button* b = new Fl_Button(10,70,100,25,"Test");
b -> callback(action,A);
G_win->show();
return(Fl::run()); }
When the button is pressed, I expect that the label in the box changes to "New" in the new bounding box whose left corner is at position x(), y() and its width and height are 110 and 50 (the dimension of the box), respectively. I call the redraw_label() function to force the drawing with the new bounding box. For what I understood, the new label should be in the top left corner of the box.
But what actually happens is that the new label is indeed "New", but its position is not changed. What am I missing here?
This question is a follow-up of this previous question: I am trying to understand how to change the position of the label with default font and then try to customize the position using non standard fonts.
The draw_label() method is intended to be used in the draw() method. If you want to trigger a draw based on a callback, this is a modification of your code:
#include <FL/Enumerations.H>
#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Window.H>
#include <FL/fl_draw.H>
class mBox : public Fl_Box {
bool triggered = false;
public:
mBox(int X, int Y, int W, int H, const char* l = 0)
: Fl_Box(X, Y, W, H, l) {};
void drawLabel() {
triggered = true;
label("New");
};
void draw() override {
Fl_Box::draw();
if (triggered) {
fl_draw_box(box(), x(), y(), w(), h(), FL_BACKGROUND_COLOR);
draw_label(x(), y(), 100, 25);
}
}
};
void action(Fl_Widget* w, void* data) {
mBox* B = (mBox*)data;
B->drawLabel();
}
int main(int argc, char** argv) {
Fl_Window* G_win;
G_win = new Fl_Window(180, 100, "The font test");
mBox* A = new mBox(10, 10, 110, 50, "The font TEST.");
A->box(FL_UP_BOX);
Fl_Button* b = new Fl_Button(10, 70, 100, 25, "Test");
b->callback(action, A);
G_win->show();
return (Fl::run());
}
Although the previous answer has been accepted I'd like to share some thoughts and code to answer the text alignment question in general. If you want to align text precisely I suggest to use fl_draw() directly to draw the box label text aligned as required rather than using draw_label(). The following code does this in the draw() method of the derived class:
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/fl_draw.H>
class mBox : public Fl_Box {
public:
mBox(int X, int Y, int W, int H, const char *l = 0)
: Fl_Box(X, Y, W, H, l){};
void draw() {
draw_box();
// arbitrary text position, change this as you need
int xo = x() + 4;
int yo = y() + h() * 2 / 3;
// measure the text extents
int dx = 0, dy = 0, tw = 0, th = 0;
fl_font(labelfont(), labelsize());
fl_text_extents(label(), dx, dy, tw, th);
// draw the green base line
fl_color(FL_GREEN);
fl_xyline(xo, yo, xo + w() - 8);
// draw the text aligned to the green base line
fl_color(labelcolor());
fl_draw(label(), x() + (w()-tw)/2, yo);
}
};
void action(Fl_Widget *w, void *data) {
mBox *B = (mBox *)data;
B->label("New");
B->redraw();
}
int main(int argc, char **argv) {
Fl_Window *G_win;
G_win = new Fl_Window(510, 150, "The font test");
mBox *A1 = new mBox(10, 10, 240, 40, "Quick fox jumps over lazy dog.");
A1->box(FL_UP_BOX);
Fl_Box *A2 = new Fl_Box(260, 10, 240, 40, "Quick fox jumps over lazy dog.");
A2->box(FL_UP_BOX);
Fl_Button *b = new Fl_Button(10, 110, 100, 25, "Test");
b->callback(action, A1);
G_win->show();
return (Fl::run());
}
I modified the callback to set the label text and call redraw(). Everything else is done in draw().
I also added a standard Fl_Box widget to show the difference.

Keep VkMainWindow on Bottom

I have an app, running on Linux, which is comprised of a VkMainWindow and several VkWindows. The desired behavior is to keep the VkMainWindow always on the bottom (and hence, all the VkWindows always on top of the VkMainWindow). The existing code works as advertised on KDE, but the customer decided it needs to run under MWM. Running under MWM, the VkMainWindow raises above the VkWindows. Any ideas?
VkMainWindow:
MainWindow::MainWindow(MyContainer const &container, ArgList args, Cardinal argc)
:
BaseWindow("My Base Window", args, argc,
_statusWindow(new StatusWindow(container)),
m_helpDialog(new MainHelpDialog),
m_container(container),
m_frame(nullptr),
m_form(nullptr),
_button1(nullptr),
_widget1(nullptr),
m_button2(nullptr),
m_widget2(nullptr),
m_button2(nullptr),
m_widget3(nullptr),
m_button3(nullptr),
m_button4(nullptr),
m_button5(nullptr),
m_widget4(nullptr),
_label1(nullptr),
_label2(nullptr),
_label3(nullptr),
_label4(nullptr),
_label5(nullptr),
_label6(nullptr),
_label7(nullptr)
{
Display *mainDisplay;
mainDisplay = XOpenDisplay(0);
if (mainDisplay)
{
m_width = m_mainWindowWidth = 1280;
m_height = m_mainWindowHeight = 1024;
XCloseDisplay(mainDisplay);
}
XtVaSetValues(m_shellWidget, XmNmwmFunctions, MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE, XmNx, 0, XmNy, 0, NULL);
m_configFile = currentWorkspace;
}
VkWindows:
BaseWindow::BaseWindow(string const &name, ArgList args, Cardinal argc,
unsigned int createOptions, bool createTabStack, bool removeCornerFunctions, bool scrollable, bool workspaceConfigurable)
:
VkWindow(name.c_str(), args, argc), _mainForm(0), _mainOffset(
MAIN_OFFSET), _buttonSpacing(BUTTON_SPACING), _createOptions(createOptions),
_createTabStack(createTabStack), _statusText(0), m_buttonBoxForm(0), m_helpForm(0),
m_okButton(0), m_cancelButton(0), m_applyButton(0), m_applyCb(0), m_okCb(0), m_cancelCb(0),
m_screenId(INVALID_SCREEN_ID), m_previousTab(0), _tabStack(0), _tabForm(0), m_initialized(false),
m_shellWidget(0), m_clipWindow(0), _isScrollable(scrollable), m_isValid(true), m_statusOnly(false),
m_validateOnOk(true), m_validateOnApply(true), m_widgetsMapped(false), m_fooLocked(false), m_isLocked(false),
m_currentSize(FULL), m_lastSize(0)
{
Widget parent = mainWindowWidget();
XtSetValues(parent, args, argc);
m_shellWidget = parent;
while (m_shellWidget && !XtIsShell(m_shellWidget))
{
m_shellWidget = XtParent(m_shellWidget);
}
if (removeCornerFunctions)
{
if (m_shellWidget)
{
XtVaSetValues(m_shellWidget,
XmNmwmFunctions, 22,
// MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE,
NULL);
XtAddEventHandler(m_shellWidget, StructureNotifyMask, false, resizeCb, this);
}
}
...
}
Main:
int main(int argc, char **argv)
{
...
Cardinal ac;
Arg args[20];
std::string title("My Client");
XrmOptionDescRec *optionList = NULL;
int numOptions = 0;
app = new VkApp(const_cast<char*>(title.c_str()), &argc, argv, optionList, numOptions);
...
app->run();
...
return (0);
}
The solution is to call VkWindow:lower() in the event loop for the main window.

How to programatically get the margin width of a QTableWidgetItem?

I think the title speaks for itself: Given a QTableWidget with items added by the setItem member function, I want to know what the margin is for each item. In particular, I want the width of the left margin in these cells.
I have prepared a little example computing text margins (a space between item rectangle and text content rectangle) of an item users clicks on:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow mainWin;
QTableWidget* table = new QTableWidget(3, 3, &mainWin);
table->setItem(0, 0, new QTableWidgetItem("Item A"));
table->setItem(1, 0, new QTableWidgetItem("Item B"));
table->setItem(2, 0, new QTableWidgetItem("Item C"));
table->setItem(0, 1, new QTableWidgetItem("Item D"));
table->setItem(1, 1, new QTableWidgetItem("Item E"));
table->setItem(2, 1, new QTableWidgetItem("Item F"));
table->setItem(0, 2, new QTableWidgetItem("Item G"));
table->setItem(1, 2, new QTableWidgetItem("Item H"));
table->setItem(2, 2, new QTableWidgetItem("Item I"));
mainWin.setCentralWidget(table);
mainWin.show();
auto slot = [&table](QTableWidgetItem* item){
QStyleOptionViewItem option;
option.font = item->font();
option.fontMetrics = QFontMetrics(item->font());
if (item->textAlignment())
option.displayAlignment = static_cast<Qt::Alignment>(item->textAlignment());
else
option.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter; // default alignment
option.features |= QStyleOptionViewItem::HasDisplay;
option.text = item->text();
option.rect = table->visualItemRect(item);
// If your table cells contain also decorations or check-state indicators,
// you have to set also:
// option.features |= QStyleOptionViewItem::HasDecoration;
// option.icon = ...
// option.decorationSize = ...
QRect textRect = table->style()->subElementRect(QStyle::SE_ItemViewItemText, &option, nullptr);
double leftMargin = textRect.left() - option.rect.left();
double rightMargin = option.rect.right() - textRect.right();
double topMargin = textRect.top() - option.rect.top();
double bottomMargin = option.rect.bottom() - textRect.bottom();
qDebug() << leftMargin;
qDebug() << rightMargin;
qDebug() << topMargin;
qDebug() << bottomMargin;
};
QObject::connect(table, &QTableWidget::itemClicked, slot);
return app.exec();
}
EDIT
To compute an exact space between table cell border and the text pixels, you have to use a QFontMetrics class.
See the QFontMetrics::leftBearing() and QFontMetrics::tightBoundingRect().

Paint device returned engine == 0, type: 1

I have seen many answers for the same Question, I have already gone through them but none them solved my problem, I am getting the error
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::end: Painter not active, aborted
I need to know, What is type : 1, and why this error showing,
My code is
iconwidget.h
class IconWigdet : public QAbstractButton
{
Q_OBJECT
QRect *iconarea;
QPainter p;
QPixmap *icon;
public:
explicit IconWigdet(QRect *rectangle,QPixmap *tempicon);
void paintEvent(QPaintEvent *);
};
iconwidget.cpp
IconWigdet::IconWigdet(QRect *rectangle,QPixmap *tempicon)
{
iconarea = new QRect();
*iconarea = *rectangle ;
icon = new QPixmap(*tempicon);
this->setGeometry(0,0,iconarea->width(),iconarea->height()+20);
}
void IconWigdet::paintEvent(QPaintEvent *)
{
qDebug() << " PaintEvent ";
p.begin(this);
p.drawText(iconarea->x()+ 10,iconarea->height()+10, "name");
p.drawPixmap ( *iconarea,*icon );
p.end();
}
groupwidget.h
class GroupWidget: public QWidget
{
Q_OBJECT
QGridLayout *groupLayout = new QGridLayout ;
QRect *rect = new QRect( 0, 0, 100, 100);
QPixmap *pimap = new QPixmap("../widgeticon/icons/ball.png");
IconWigdet *icon = new IconWigdet(rect,pimap);
public:
GroupWidget();
};
groupwidget.cpp
GroupWidget::GroupWidget()
{
groupLayout->addWidget(icon, 0, 1, 1, 1, 0);
this->setLayout(groupLayout);
icon->show();
QPaintEvent *e;
icon->paintEvent(e);
}
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GroupWidget *Gw = new GroupWidget;
Gw->show();
return a.exec();
}
and the iconwidget class will work perfectly if the main function changed to
int main(int argc, char *argv[])
{
QApplication a(argc, argv);;
QRect *rect = new QRect( 0, 0, 100, 100);
QPixmap *pimap = new QPixmap("../widgeticon/icons/ball.png");
IconWigdet *Iw = new IconWigdet(rect,pimap);
Iw->show();
return a.exec();
}
that means, If we use iconwidget class object in main function it is working, but it is not working when we do the same in groupwidget class,
Thanks in advance
You're calling IconWigdet::paintEvent directly. This is not allowed. Instead of calling it directly, call QWidget::update or QWidget::repaint.
GroupWidget::GroupWidget()
{
groupLayout->addWidget(icon, 0, 1, 1, 1, 0);
this->setLayout(groupLayout);
icon->show();
// QPaintEvent *e;
// icon->paintEvent(e); this is not allowed
icon->update(); // do this instead
}
Though I don't see why would you call anything there. Just calling Icon->show(); should be enough. Qt will automatically schedule a paint event.
Try this instead of your constructor
GroupWidget::GroupWidget()
{
groupLayout->addWidget(icon, 0, 1, 1, 1, 0);
this->setLayout(groupLayout);
setMinimumSize(100,100);
setMaximumSize(200,200);
icon->show();
}
This will work,

XNextEvent Blocking At Start of Application

I have the following application.
#include <FWWindow.h>
#include <FWApplication.h>
int main(int /*argc*/, char */*argv*/[])
{
FWApplication::Initialize();
FWWindow *win = new FWWindow(800, 600);
win->Show();
FWApplication::Run();
delete win;
}
When I run it it gets stuck on the XNextEvent() because it blocks until it gets the next event from the XServer. What I would like to know, is based on the code below, why is the XNextEvent not getting the ConfigureNotify or Expose events after I am calling XMapWindow(); I have checked to make sure my application provides the right Display based on the address in the watch window of my IDE. What am I missing to get the window to appear?
Initialize() does the following
-
FWApplication *FWApplication::Initialize()
{
if (!_instance)
{
_xDisplay = XOpenDisplay(NULL);
if (_xDisplay == NULL)
throw "Failed to get XDisplay";
_initialized = true;
_instance = new FWApplication(); // Calls an empty ctor
}
return _instance;
}
FWWindow *win = new FWWindow(800, 600); does the following
-
FWWindow::FWWindow(int width, int height) :
clientWidth(width),
clientHeight(height)
{
// These are all member variables
xDisplay = FWApplication::GetMainDisplay();
xScreen = DefaultScreen(xDisplay);
xDepth = DefaultDepth(xDisplay, xScreen);
xVisual = DefaultVisual(xDisplay,xScreen);
xAttributes.background_pixel = XWhitePixel(xDisplay, xScreen);
xAttributes.border_pixel = XBlackPixel(xDisplay, xScreen);
xAttributes.override_redirect = 0;
xWindow = XCreateWindow(
xDisplay,
RootWindow(xDisplay, xScreen),
0, 0,
width, height,
0,
xDepth,
InputOutput,
xVisual,
CWBorderPixel | CWColormap | CWEventMask,
&xAttributes
);
XSetStandardProperties(
xDisplay,
xWindow,
"glxsimple",
"glxsimple",
None,
NULL,
0,
NULL
);
}
win->Show(); does the following
-
void FWWindow::Show()
{
XMapWindow(xDisplay, xWindow); // xWindow and xDisplay defined in ctor above
}
And finaly FWApplication::Run(); does the following
-
int FWApplication::Run()
{
if (!_initialized)
return -1;
static bool run = true;
static Display *lDisplay = _xDisplay;
XEvent xEvent;
while (run)
{
do
{
XNextEvent(lDisplay, &xEvent);
switch (xEvent.type)
{
case ConfigureNotify:
{
unsigned int w = xEvent.xconfigure.width;
unsigned int h = xEvent.xconfigure.height;
// Do something to main widget
}
case Expose:
break;
}
} while (XPending(GetMainDisplay()));
}
return EXIT_SUCCESS;
}
You're not specifying an event mask in your window's attributes, so XNextEvent() won't report any event. You should write something like:
xAttributes.background_pixel = XWhitePixel(xDisplay, xScreen);
xAttributes.border_pixel = XBlackPixel(xDisplay, xScreen);
xAttributes.override_redirect = 0;
xAttributes.event_mask = StructureNotifyMask // for ConfigureNotify
| ExposureMask; // for Expose