QTabWidget - how to "include" a pointer to every tab? - c++

I'm trying to make a simple communicator, with UI based on tabs (QTabWidget). I want tabs to be closeable and movable. Still, for every tab I would like to remember a pointer to my class (where I keep socket etc.), so I could manage closing tabs and disconnecting sockets.
One way is to keep them(pointers) in array / any container, analyze any move that was done by a user, and change indexes or swap pointers dependently on index of tabs, that were moved, but this involves a lot of work, and even more bugs. Is there any other and simpler way I could get it?

Use myTabWidget->widget(index).
There is one for each tab.
Doc
You can set the widget as the parent of your class if your class inherits from QObject, or connect its signals (like destroyed()) with that of your class.
Or you can even do
QVariant prop = QVariant::fromValue<intptr_t>((intptr_t)workerObject);
myTabWidget->widget(index)->setProperty("workerObject", prop);
to really store the pointer, and
QVariant prop = myTabWidget->widget(index)->getProperty("workerObject");
WorkerClass *ptr = (WorkerClass*) prop.value<intptr_t>();
to get it back.

Related

Any instance access all instances (of a class)

This may seem like a trivial question, or I may have misunderstood previous information/the research I've done so far.
But is it possible to have a object with a function (in C++) that can access all instances of its own type?
In the context of my usage. I wanted to have a Button class, whereby I could simply instantiate multiple Buttons but call to a function could call reference all buttons.
ButtonInstance.isMouseTargetting(cursorCoordinates);
Is this possible? If so is it efficient?
Or should I have the class which owns the Button instances call each instance to check if the mouse coordinates match up?
I'm under the impression you are looking for advice on how to design this.
In the context of my usage. I wanted to have a Button class, whereby I
could simply instantiate multiple Buttons but call to a function could
call reference all buttons.
You want to do this in a button container. A button is not a button container and in a GUI context you already have an established hirerarchy.
Or should I have the class which owns the Button instances call each
instance to check if the mouse coordinates match up?
Yes. You probably already have a window/container class for this.
Your question is more of about Design pattern than C++ itself. Take a look at the Gang of Four book;you will find an appropriate implementation.
You can, for example, make a list of all objects created for a given class,
class Button {
public:
Button() {
_buttonList.push_back( this );
// assign index/handler to this button
}
~Button() {
_buttonList.erase( _handle );
}
static bool isMouseTargeting( float x, float y ) {
for ( auto button : _buttonList ) {
// check if inside
}
return false;
}
private:
static std::list< Button* > _buttonList;
// Handler _handle;
}
This is only a very general example of what you could do. You can use any other container besides a list (entirely up to you), and you have to find a way to index each button (or create a handle) so that you can later erase it in the destructor.
Beware of the default constructors (copy or move). If you don't explicitly create your constructors then some of your buttons will not enter the list, so either make them yourself or delete them.
Button( const Button& button ) = delete;
This is one way to do what you asked, but not necessarily the best solution. It may be simpler to just add the buttons to a non-static container by yourself and search from there.
The short answer is yes. But i will not recommend to put this functionality on the Button class since this will add extra (maybe not expected) responsibility to it. You can achieve the desired behavior by storing your Button objects on some collection and then call a function to check which button is targeted by the mouse.
Another solution would be to store the buttons collection as a member of a higher level class that represents your user-interface. This way you can call a method of this class and check if the mouse cursor is currently on some Button or not. With this design you can add the same support for other GUI elements (if you need to) easily.

QT add widgets to UI anywhere

The application that I'm building is supposed to create, destroy, and manipluate widgets that I've created
The problem is I'm not making a simple program with nice buttons where everything is symmetrical and needs to be evenly spaced and handled via a layout that will automatically move everything around and space it.
And yet, the only way I know of is to manually instance a layout and add the widgets to it, but then I can't set the coordinates of them
How can I simply instance my widget, and add it to the project generated frame?
This is how I'm instantiating my class, in which case I then set my own parameters:
Tile *tile = new Tile;
tile->setImg("://Images/placeholderTile.png");
tile->setCol(true);
tile->setGeometry(retX(line),retY(line),50,50);
To reiterate, I want to add my own widgets to a frame outside of the editor (only by code), and be able to manually move them around the frame by code.
I don't see an ".addWidget() as a method accessible from the QFrame, and yet they can be children within the designer, why can't I do this by code? Whenever I try to do it manually and add them to any layout, any attempt I make to manually set the widgets location doesn't do anything. I haven't overridden the setGeometry
I fixed my problem
After 2 hours of continual searching I finally came across my answer
I never thought that you could set the parent of a widget by code, as I thought you strictly had to add it in as a child of something else, not the reverse and declare that it should have a parent
So, by simply adding the line:
tile->setParent(ui->frame);
completely fixed my problem.
I will change this post back and submit the answer tomorrow when I'm allowed to by this site.
Thank you to those who actually came though. I'm just glad I managed to fix it before that.
All you need is to pass the parent to the widget's constructor:
Tile *tile = new Tile(ui->frame); // <-- here
tile->setImg("://Images/placeholderTile.png");
tile->setCol(true);
tile->setGeometry(retX(line),retY(line),50,50);
Since Tile is your own class, you should definitely have a Qt-style, parent-taking explicit constructor for it:
class Tile : public QWidget {
...
public:
explicit Tile(QWidget * parent = 0) : QWidget(parent) { ... }
};
Another approach is to write your own layout that would know about the relationships that are to be held between your objects. After you do it once, writing custom layouts isn't that hard.

Qt C++; How to address a certain textfield, depending on user input

I'm having issues with formulating the question but this is what I want to do with my application.
A user can select one or multiple image-files (.ppm), and they are displayed in some sort of legend, with their filename underneath. The information of these images is stored in a structure. (This structure contains the image path, name, and other info).
Now I want to give the user the chance to change the name of the selected images, and uses this name in the rest of the application. So I would have to change the name in the structure.
I could do this by adding textfields in the legend, where users can type the desired name, but how can I get the input from this textfield if I don't know which one is alterred?
If the user selects 6 images, I need 6 new textfields in the legend, but how can I address the correct one?
struct[2].name = input2.getText();
I also thought about doing it with some sort of wizard, with 6 pages where the names can be changed, but I don't know how I can adress the correct textfield.
Any help would be welcome, thanks!!
If you want to allow users to rename multiple files at one time, you may want to create a wizard. In the wizard you could display each picture they selected (one at a time) and allow them to rename each picture (one at a time). Otherwise it will be confusing to the user and harder for you to manage.
When generating the wizard, I would use the information structure to associate the picture with the textfield.
Qt, signals and slots are your friend here.
When you setup the textfields for the name, assuming you use something like a QLineEdit object, connect to a relevant signal, such as editingFinished(). Make the connection to the slot of the Object that stores the structure. The receiving slot then updates the appropriate information.
So, assuming your struct is in an object derived from QObject, you can do something like this: -
struct DataStruct // the struct storing the underlying data
{
QString name;
QLineEdit* linkedEditWidget; // widget for user to change text
};
class MainObject : public QObject
{
Q_OBJECT // required for signals and slots
public slots:
void UpdateText();
private:
const int NUM_STRUCTS = 10; // initialisation in C++ 11
DataStruct myStructs[NUM_STRUCTS]; // a number of structs
};
When you initialize the array of structs and the LineEdit widgets, store a pointer to the matching LineEdit widget in each myStruct and connect the widgets' editingFinished signals to the MainObject updateText() slot. It would be a good idea to use weak smart pointers here, but I'll use a standard pointer, to keep things simple.
When you receive notification that the text has changed, you'll need to match up the caller with the LineEdit* in the struct. Note that QObject::sender() will return the pointer to the object that sent the message: -
void MainObject::UpdateText()
{
QObject* theSendingWidget = sender();
for(int i=0; i<NUM_STRUCTS; ++i) // assuming NUM_STRUCTS is already defined
{
if(myStructs[i].linkedEditWidget == theSendingWidget)
{
// update the name in the data struct
myStructs[i].name = (static_cast<QLineEdit*>(theSendingWidget))->text();
return; // our work is done.
}
}
}
Finally, you'd probably make life easier for yourself by storing the data in Qt model objects, rather than using a plain struct. I suggest reading up on Model / View programming and the MVC design pattern

qt/c++ naming of variables dynamically

I am in the process of developing a html editor in Qt for one of my university assignments, and i am having a problem regarding naming of some variables.
the problem is this:
when the user decides to load their "project" the program iterates through the folder and finds how many .html files are in there, it then creates tabs for them to be displayed in.
I have a custom QTextEdit which has a customer completer and syntax highlighting etc. the problem i am having at the moment is how to create them depending on the number needed.
i create a QStringList of file names:
QStringList m_files;
m_files = aDialog.m_loadDirectory->entryList(QStringList("*.html"),QDir::Files|QDir::NoSymLinks);
then i iterate through each one of the list:
for(int i=0; i<m_files.count();i++)
{
}
and for each one i need to create a new custom QtextEdit
TextEdit *name = new TextEdit;
then add to the tab
tabs->addTab(name,"someTitle");
but as each TextEdit needs to be different for each tab (i think this is correct) i need a different Variable name for each one.
i thought about creating a list/array of TextEdit objects but as i dont know how many i need to use, i could end up easily with too many (wasted memory) or not enough..
any ideas on how i can get around this?
one thought..
would it be possible to create a TextEdit object before the loop
then make a copy of that object in the loop and add the copied object to the tab? (still variable naming problem...)
thanks
but as each TextEdit needs to be different for each tab (I think this is correct)
Yes, you need a different TextEdit in each tab.
I need a different Variable name for each one.
No, you don't need a different variable name for each one. You need different objects, but variable names don't have much to do with that.
A simple:
for (...) {
TextEdit *te = new TextEdit(...);
// set up that text edit in whatever way you need
tabs->addWidget(te, "foo");
}
does exactly what you want. The variable name te is completely irrelevant (it won't even appear in the executable outside of debugging symbols). Each time through the loop, you'll be working on a separate TextEdit instance.
If you need to refer to that TextEdit by name at runtime, you can keep all your widgets in a collection, a QMap for instance.
QMap<QString, QWidget*> all_editors;
...
for (...) {
TextEdit *te = ...;
all_editors[filename] = te;
...
}
You have discarded quickly the only viable solution : put your text edits in a collection. The textedit have to be created with new, so the collection itself will not waste space.
You can use a QPair<QTabWidget*, QTextEdit*> for simplest cases. For more complicated cases create a custom widget, and just make a list of those.
Copying a QObject is a really bad idea. I think the copy constructor is private so you will not even be able to do that

Cannot copy Qwidgets in tab to a new tab

Ok so I have been going insane trying to find an answer to this for a day now. What I am trying to do is make a copy of all the widgets in a tab. I want to transfer the copy to a new tab. Think of a form in a tab, and when you click "New Tab" it displays the same form but blank. I am new to QTCreator so any pointers would be great.
Thanks in advance!
Any class that derives from QObject is not copyable. If you want to "copy" a widget, then perhaps a model-view architecture would be a better fit where you have two different views representing the model.
Another thought: you could have each class that needs to be copyable create a state object that could then be used to set the state on the copy.
Since you're just trying to display the same form in multiple places, you could do something like this.
First, create your form which I'll assume is called MyForm:
class MyForm: QWidget {...};
Then, in the parent form:
void ContainerForm::ContainerForm(...) {
connect(pbAddNewTab, SIGNAL(clicked()), SLOT(addNewTab()));
}
void ContainerForm::addNewTab() {
tabWidget->addTab(new MyForm(this));
}
You may need to pull out the new so you can setup signals and slots, etc.
If the new form is blank then it is not a copy. All you need to do is create a new instance of your form widget.