How can I cast Gtk::Widget to GtK::ScrolledWindow on gtkmm? - c++

On the code bellow I am setting ScrolledWindows on a grid container and then setting their properties in a loop, because I want to put TextViews on the grid. I do not want to use TreeView because it will be ackward to put long or moderate texts there, which is what I am intending. Unfortunately the function get_child_at(int col,int row), for getting the attached widgets returns Gtk::Widget and for the properties I need I must do a cast for Gtk::ScrolledWindow; which I am not being able to do.
(some code)
descriptors->attach(* (new Gtk::ScrolledWindow ()), 0,0);
descriptors->attach(* (new Gtk::ScrolledWindow ()), 1,0);
descriptors->attach(* (new Gtk::ScrolledWindow ()), 2,0);
(some other code)
for(int i = 0; i < 3; i++)
{
//Glib::RefPtr<Gtk::ScrolledWindow> * disposable_pointer = new Glib::RefPtr<Gtk::ScrolledWindow>();
//disposable_pointer = descriptors->get_child_at (i,0);
//Glib::RefPtr<Gtk::ScrolledWindow> disposable_pointer = Glib::RefPtr<Gtk::Widget>::cast_dynamic(*descriptors->get_child_at (i,0));
//Glib::RefPtr<Gtk::ScrolledWindow>Glib::RefPtr<Gtk::ScrolledWindow>::cast_dynamic
//current_grid_child_scrolledWin = Glib::RefPtr<Gtk::ScrolledWindow>cast_dynamic(descriptors->get_child_at (i,0));
//current_grid_child_scrolledWin = disposable_pointer->get();
Glib::RefPtr<Gtk::Widget> * something = new Glib::RefPtr<Gtk::Widget>(descriptors->get_child_at (i,0));
Glib::RefPtr<Gtk::ScrolledWindow> disposable_pointer = *something;
current_grid_child_scrolledWin->set_vadjustment (Glib::RefPtr<Gtk::Adjustment>());
current_grid_child_scrolledWin->set_hadjustment (Glib::RefPtr<Gtk::Adjustment>());
current_grid_child_scrolledWin->set_policy (Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
current_grid_child_scrolledWin->set_vexpand (true);
current_grid_child_scrolledWin->set_hexpand (true);
current_grid_child_scrolledWin->set_margin_end (10);
current_grid_child_scrolledWin->set_margin_bottom (10);
current_grid_child_scrolledWin->set_visible (true);
}
the last error I got was
/usr/include/glibmm-2.4/glibmm/refptr.h:309:31: error: invalid conversion from 'Gtk::Widget*' to 'Gtk::ScrolledWindow*' [-fpermissive]
309 | pCppObject_(src.operator->())
| ^
| |
| Gtk::Widget*
I considered the gtkmm documentation on using casting with Glib::RefPtr

As we can see from the Gtk::Grid documentation, the return type of get_child_at is a raw pointer to a widget:
Widget* Gtk::Grid::get_child_at(int column,
int row
)
and not a smart pointer (in your case a Glib::Refptr). This is because the caller of get_child_at is not responsible to manage the lifetime of the object pointed to by the returned pointer. You simply get a handle, work on it and then leave it alone. Someone else is responsible to call delete on it. Here is how I would do it in your case:
#include <gtkmm.h>
#include <iostream>
int main(int argc, char *argv[])
{
// Initialize Gtkmm:
auto app = Gtk::Application::create(argc, argv, "so.question.q66162108");
// Create a grid and attach a scrolled window:
Gtk::Grid container;
Gtk::ScrolledWindow scrolledWindow;
// Add the scrolled window to the grid. Make sure the container is responsible
// of deleting it! Otherwise, remeber to call delete on it:
container.attach(*Gtk::manage(new Gtk::ScrolledWindow()), 0, 0, 1, 1);
// Here, you get a raw pointer to a widget:
Gtk::Widget* pWidget = container.get_child_at(0, 0);
// We cast it to its most derived type:
Gtk::ScrolledWindow* pScrolledWindow = dynamic_cast<Gtk::ScrolledWindow*>(pWidget);
// If the cast fail (i.e. not a ScrolledWindow at (0, 0), then the
// casted pointer will be set to nullptr:
if(pScrolledWindow)
{
std::cout << "Casting worked" << std::endl;
// Handle properties here...
pScrolledWindow->set_visible (true);
// Set other properties here...
}
return 0;
} // At this point the container is destroyed and since Gtk::manage
// was used, the scrolled window will be automatically deleted.
Notice that nowhere is this code I am using a smart pointer. This is because when I new my Gtk::ScrolledWindow, I use Gtk::manage*. This function marks the object (here the Gtk::ScrolledWindow) as owned by its parent container widget (Here the Gtk::Grid), so you don't need to delete it manually. I becomes the container's responsibility to call delete on its children when it goes away.
*If you use a newer Gtkmm version, you will have to use make_managed<T> instead. See here.

Related

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;
}

When exactly are published properties assigned at run time?

I have a custom control which needs to do some things when loading at run time, based on a published property. However, I am running into a problem where, whenever I check the published property, it has not been set yet, and is always the default value.
I first attempted checking the property in the constructor, of the control, but quickly found they were not loaded yet. I know that when the control is shown on the screen the properties are set correctly, thus its not an issue with the properties not being loaded at all.
I next attempted overriding the Loaded Method but am still having the same problem, so I don't think this is exactly what I am looking for.
void __fastcall TFmSearchBar::Loaded()
{
TEdit::Loaded(); //call base class loaded
if( MyProperty )
{
//do stuff
}
}
At what point are these published properties actually getting set?
What method can/should I hook into in order to execute some logic in my control based on these properties, as soon as the properties are set correctly?
If I check the property in the constructor of the control, the property is always the default value even if I have specified otherwise in the designer.
Correct, because its design-time values have not been assigned yet.
At what point are these published properties actually getting set?
When the Owner (Form, Frame, or DataModule) is being constructed. It loads its own DFM resource and parses it, constructing stored child components and reading their property values.
For example, say you have the following DFM:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
...
object Edit1: TEdit
Left = 136
Top = 64
Width = 121
Height = 21
TabOrder = 0
end
object Button1: TButton
Left = 263
Top = 62
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 1
end
end
The DFM streaming process roughly translates to the following equivalent code (I'm leaving a lot of internal details out for simplicity):
__fastcall TCustomForm::TCustomForm(TComponent *Owner)
: TScrollingWinControl(Owner)
{
this->FFormState << fsCreating;
try
{
// locate, load, and parse the "Form1" DFM resource ...
this->FComponentState << csLoading;
this->Parent = ...;
this->Name = L"Form1":
this->FComponentState << csReading;
this->Left = 0;
this->Top = 0;
this->Caption = L"Form1";
...
TEdit *e = new TEdit(this);
try
{
e->FComponentState << csLoading;
e->Parent = this;
e->Name = L"Edit1"; // <-- sets the derived Form's 'Edit1' member to this object
e->FComponentState << csReading;
e->Left = 136;
e->Top = 64;
e->Width = 121;
e->Height = 21;
e->TabOrder = 0;
e->FComponentState >> csReading;
}
catch (...)
{
delete e;
throw;
}
TButton *b = new TButton(this);
try
{
b->FComponentState << csLoading;
b->Parent = this;
b->Name = L"Button1"; // <-- sets the derived Form's 'Button1' member to this object
b->FComponentState << csReading;
b->Left = 263;
b->Top = 62;
b->Width = 75;
b->Height = 25;
b->Caption = L"Button1";
b->TabOrder = 1;
b->FComponentState >> csReading;
}
catch (...)
{
delete b;
throw;
}
this->FComponentState >> csReading;
...
e->Loaded();
b->Loaded();
this->Loaded();
}
__finally
{
this->FFormState >> fsCreating;
}
}
So, as you can see, a component's property values are not available yet when its constructor is called.
What method can/should I hook into in order to execute some logic in my control based on these properties, as soon as the properties are set correctly?
That depends on what the properties need to do. If they need to perform operations immediately, you can do that directly in their property setters. But if they need to wait until other properties have been loaded first (if one property is dependent on the value of another property), then override the virtual Loaded() method instead, which is automatically called after DFM streaming is finished. Property setters can check the flags of the ComponentState property to know whether or not the component is currently running in the Form Designer at design-time, whether or not a DFM is currently being streamed, etc and then act accordingly as needed.
I attempted overriding the Loaded Method but am still having the same problem
Which is what exactly? You did not explain what your actual problem is. Please edit your question to provide those details.
so I don't think this is exactly what I am looking for.
It most likely is, you probably are just not using it correctly.

Deleting a widget from QTableView

I have a delete button(QPushButton) in the last column of each row of my table view. I am creating these push buttons and directly setting them in view. Since I have allocated memory dynamically I wish to free this memory but I haven't stored pointers of these buttons anywhere so I am trying to obtain the widget at the time of clean up and deleting them.
SDelegate* myDelegate;
myDelegate = new SDelegate();
STableModel* model = new STableModel(1, 7, this);
myWindow->tableView->setModel(model);
myWindow->tableView->setItemDelegate(myDelegate);
for(int i = 0; i < no_of_rows; ++i) {
QPushButton* deleteButton = new QPushButton();
myWindow->tableView->setIndexWidget(model->index(i, 6), deleteButton);
}
exec();
// Cleanup
for(int i = 0; i < no_of_rows; ++i) {
// code works fine on removing this particular section
QWidget* widget = myWindow->tableView->indexWidget(model->index(i, 6));
if (widget)
delete widget;
}
delete model;
delete myDelegate;
I am getting a crash in qt5cored.dll (Unhandled exception) and application is crashing in qcoreapplication.h at the following code:
#ifndef QT_NO_QOBJECT
inline bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{ if (event) event->spont = false; return self ? self->notifyInternal(receiver, event) : false; }
While debugging there is no issue in deleting these widgets but code crashes afterwards at some other point. I am using QTableView and custom class for model which has inherited QAbstractTableModel.
There's a Qt bug that manifests as follow: if there are any index widgets, and you invoke setModel(nullptr) on the view, it'll crash in an assertion on visualRow != -1 in qtableview.cpp:1625 (in Qt 5.6.0). Presumably this bug could be triggered when the model is being removed in some other fashion too.
But I can't reproduce it by merely destroying the model instance. So I doubt that it's relevant here unless you get the same assertion failure.
Given the style of your code, it's more likely that you have a memory bug elsewhere. If you think that the code above is crashing, you should have a self-contained test case that demonstrates the crash. Is your model or delegate to blame? Would it crash using no delegate? Would it crash using a stock model?
Your code excerpt seems to be fine, if mostly unnecessary. You could allocate the delegate and the model locally. The buttons are owned by the view: as soon as the need for the buttons goes away, such as when the model changes the row count or goes away, they will get appropriately deleted. So you don't have to delete them yourself, it's safe but completely unnecessary.
Here's an example that demonstrates that in all cases, the buttons will get disposed when the model gets destroyed or the view gets destroyed, whichever comes first. Tracking object lifetime is super simple in Qt: keep a set of objects, and remove them from the set using a functor attached to the object's destroyed signal. In Qt 4 you'd use a helper class with a slot.
// https://github.com/KubaO/stackoverflown/tree/master/questions/model-indexwidget-del-38796375
#include <QtWidgets>
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QSet<QObject*> live;
{
QDialog dialog;
QVBoxLayout layout{&dialog};
QTableView view;
QPushButton clear{"Clear"};
layout.addWidget(&view);
layout.addWidget(&clear);
QScopedPointer<QStringListModel> model{new QStringListModel{&dialog}};
model->setStringList(QStringList{"a", "b", "c"});
view.setModel(model.data());
for (int i = 0; i < model->rowCount(); ++i) {
auto deleteButton = new QPushButton;
view.setIndexWidget(model->index(i), deleteButton);
live.insert(deleteButton);
QObject::connect(deleteButton, &QObject::destroyed, [&](QObject* obj) {
live.remove(obj); });
}
QObject::connect(&clear, &QPushButton::clicked, [&]{ model.reset(); });
dialog.exec();
Q_ASSERT(model || live.isEmpty());
}
Q_ASSERT(live.isEmpty());
}
Check out QObject::deleteLater() , it usually helps with issues around deleting QObjects / QWidgets.

Why don't ncurses widgets work properly when created in a separate function?

I'm trying to create a series of nested menus using pure ncurses in C++. If I create a menu and post it in main(), it works fine. But if I take the same code and put it in a function that returns a MENU*, it doesn't work at all. Am I missing something?
Code that works:
int main()
{
/*
* snipped out the curses startup code
*/
vector<char*> options;
options.push_back("List");
options.push_back("Add");
options.push_back("Delete");
options.push_back("Exit");
vector<ITEM*> menu_items(options.size());
for (int i = 0; i < options.size(); i++)
menu_items[i] = new_item(options[i], NULL);
MENU *options_menu;
options_menu = new_menu(&menu_items[0]);
set_menu_win(options_menu, main_window);
set_menu_sub(options_menu, derwin(main_window, 6, 20, 3, 3));
set_menu_mark(options_menu, ">");
refresh();
post_menu(options_menu); // this works fine
wrefresh(main_window);
/*
* snipped out the rest of the stuff
*/
}
Code that doesn't work:
MENU *make_menu()
{
/*
* same as code in previous main()
*/
return options_menu;
}
int main()
{
/*
* snip
*/
MENU *options_menu = make_menu();
refresh();
post_menu(options_menu); // this doesn't do anything
wrefresh(main_window);
/*
* snip
*/
}
I'll answer this for future searchers. It turns out that new_menu takes a pointer to an ITEM list and hangs onto it. If the ITEM list is on the stack of a function, it'll get deleted when the function returns, making the ITEM list invalid.
The solution to this problem is to create the ITEM list on the heap, either via new in C++, or using malloc (remember to delete or free!). Another option is to wrap the MENU in a class and keep the ITEM list as a member variable.
The simplest solution is to just make the MENU (or ITEM list) global.
Undefined behavior: new_menu expects a NULL-terminated array of ITEM* as input, you need a menu_items.push_back(0);.

wxWidgets wxTextCtrl crash when deleting

wxTextCtrl causes some memory allocation problem when tried to be deleted or it's value changed. Here's some code insight:
wxTextCtrl* s = new wxTextCtrl(...);
s->SetValue("abc");//crash
delete s//crash
It's like all of its members are const's. Here is what VisualStudio says when it crashes:
An unhandled exception of type 'System.AccessViolationException'
occurred in Unknown Module.
Additional information: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
And even when i try the wxWidgets default destroy:
parent->DestroyChildren(); //ofc the parent is wxPane passed in constructor of s
Any help will be appreciated.
Here's some actual code from the only functions calling wxTextCtrl:
void AddButton::OnAction(wxSize* frame){
if ( !DoAction ){
if ( ! thy )
{
thy = new wxPanel
(mParent, -1,
wxPoint(0, 0),
wxSize(PanelWidth, mParent->GetSize().GetHeight()),
wxBORDER_NONE | wxFRAME_FLOAT_ON_PARENT );
thy->SetBackgroundColour(wxColor(30,30,30));
thy->Show();
if ( ! AddPanelDialog ){
//AddPanelDialog = (new _Text
//(this, thy, "add link...", wxPoint(1, 30), wxSize(PanelWidth - 30, 20),
//wxBORDER_NONE | wxTE_PROCESS_ENTER ));
wxTextCtrl* s = new wxTextCtrl(thy, -1, "", wxPoint(1, 30), wxSize(PanelWidth - 30, 20),
wxBORDER_NONE | wxTE_PROCESS_ENTER );
s->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(_Text::OnEnter));
s->Show();
}
if ( !ConfirmPanel ){
ConfirmPanel = new wxPanel
(thy, -1, wxPoint(PanelWidth - 28, 30), wxSize(27, 20),
wxBORDER_NONE | wxFRAME_FLOAT_ON_PARENT );
ConfirmPanel->SetBackgroundColour(wxColor(38, 145, 232));
ConfirmPanel->Show();
}
}
else {
thy->Show();
}
gui* rmd = (gui*)mParent;
rmd->LeftPanelActivate();
rmd->SetNewPositions(rmd->GetParent()->GetSize());
Button::Update();
helper::SendRedrawEvent(mParent);
DoAction = true; // indicates action activated
}
else{
thy->Hide();
gui* rmd = (gui*)mParent;
rmd->LeftPanelActivate(false);
rmd->SetNewPositions(rmd->GetParent()->GetSize());
Button::Update();
helper::SendRedrawEvent(mParent);
DoAction = false; // indicates action activated
}
}
and function that calls SetValue()
void AddButton::OnEnter(wxCommandEvent& event)//enter button handler
{
wxTextCtrl* _t = (wxTextCtrl*)this;
_Clear();
*_t<<"sup";
}
I think you have a problem with understanding of Connect(). If you're using it to connect to a method of a different object, you must pass a pointer to this object as the last argument (called eventSink in the documentation). Your method is almost certainly being called on a wrong object pointer.
And you should absolutely never, ever have to cast this like you do in OnEnter().
Are you sure that you really need to delete the wxTextCtrl? If this text control is placed into sizer then sizer is responsible for it and will destroy it when needed. You probably need to detach the text control from sizer and then delete it.
Also you should use Destroy() method instead of delete operator. This is clearly explained in docs.
As for crash on SetValue() call: have you tried to use wxT("abc")? Also what version of wxWidgets, OS and compiler are you using? Haven't experienced such problems with wxWidgets at all. Maybe you could post some meaningful piece of code which can help to identify the problem?