Naming/Referencing QPushButton in QTableWidget Cell (setCellWidget question)



  • Hi,

    So I am trying to put some buttons into a entire column for use in my UI. By reading other posts I found that (example for first row, first column):

    ui->imgTable->setCellWidget ( 0, 0, new QPushButton(ui->imgTable))
    

    does the trick. I am not sure the part in the parenthesis is correct but I believe that it should set it so that the Table is the parent of the button.

    Upon running the code I do get a button in the cell just like I would want to. However there is something I am clueless towards that I cannot seem to find an answer for. How do I give the QPushButton object a name (i.e. myButton or imgPshButton) so that I can refer to it and changes its attributes like its text?

    If were to make a push button this way, giving it a name and messing with its parameters are obvious:

    #include <QPushButton>
    QPushButton myButton;
    myButton.setText("text")
    

    But doing that when creating the button using setCellWidget seems impossible. I would assume it may give it some default name like "pushbutton" but I don't know how to check that and I would like to name it something specific. I tried to make the button first and then pass its name into setCellWidget function instead of "new QPushButton(ui->imgTable)" but I get "Cannot convert from QPushButton to QWidget" or something to that effect so that isn't right.

    Any ideas?

    Thanks.


  • Moderators

    Well, you just create you button and pass the pointer to setCellWidget(...):

    QPushButton *myButton = new QPushButton(ui->imgTable);
    // Do here something with the button
    ui->imgTable->setCellWidget ( 0, 0, myButton);
    

    You can put the QPushButton *myButton into your class private section if you need the pointer later.
    Actually there is no need to keep the pointer - you can get it via cellWidget(...).



  • @jsulm Ah thank you, I was actually just reading something that sorta explained this a moment ago. I tried basically what you said but didn't pass the pointer and was trying to pass it directly.

    I do actually need the pointer later (I think) so to expand a question into what you stated, how exactly would I refer to the button in a different location of the program (i.e. another function).

    For example, my code right now is:

    QPushButton * PshBtnImgSel [6] = {new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add")};
    qDebug() << PshBtnImgSel[0]->text();
    

    which outputs "Add", but if I try using that qDebug() statement outside the MainWindow class in a function that I created I get:

    error: C2065: 'PshBtnImgSel': undeclared identifier
    

    which makes sense to me because the QPushButton i declared earlier is not in the scope of my function. So it sounds like there is a way to refer to the button directly from the UI widget that is created for it using cellWidget(...).

    How would I refer to it, possibly using cellWidget, so that I could read or write the button text in a different function? It would be very ugly if I actually had to pass the buttons around through every function that needs to refer to them.


  • Moderators

    If you need it in a different function then just pass the pointer as parameter to that function.
    In general you should not expose internal class details (like these buttons) to other classes/functions. Instead you should either provide an interface in your class or use signals/slots.

    So, why do you want to access these buttons from outside of the MainWindow class?



  • @jsulm Sorry, this comes from the fact I haven't worked with C++ in a long time and never really got into advance program structures with classes, etc.

    So I thought by declaring the function as a member of MainWindow that it wouldn't be outside the class, but I am not sure because the error remains. Right now I basically have a function that tries to read the text from one out of six buttons, and basically just swtiches the text back and forth between two options. If the button says "Add" it will change it to "Remove" and if it says "Remove" it will change it to "Add". Here is the function in question:

    void MainWindow::fnImgSelBtnTextSwitch(int BtnNum)
    {
        if(QString::compare("Add",PshBtnImgSel[BtnNum]->text()))
        {
            PshBtnImgSel[BtnNum]->setText("Remove");
        }
        if(QString::compare("Remove",PshBtnImgSel[BtnNum]->text()))
        {
            PshBtnImgSel[BtnNum]->setText("Add");
        }
    }
    

    Here is where I initially setup the buttons:

    QPushButton * PshBtnImgSel [6] = {new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add")};
    
    for(int i = 0; i < 6; i++)
    {
    ui->imgTable->setCellWidget( i, 0, PshBtnImgSel[i]);
    }
    
    QObject::connect(PshBtnImgSel[0], SIGNAL(clicked()),this, SLOT(on_PshBtnImgSelZro_clicked()));
    QObject::connect(PshBtnImgSel[1], SIGNAL(clicked()),this, SLOT(on_PshBtnImgSelOne_clicked()));
    QObject::connect(PshBtnImgSel[2], SIGNAL(clicked()),this, SLOT(on_PshBtnImgSelTwo_clicked()));
    QObject::connect(PshBtnImgSel[3], SIGNAL(clicked()),this, SLOT(on_PshBtnImgSelThr_clicked()));
    QObject::connect(PshBtnImgSel[4], SIGNAL(clicked()),this, SLOT(on_PshBtnImgSelFor_clicked()));
    QObject::connect(PshBtnImgSel[5], SIGNAL(clicked()),this, SLOT(on_PshBtnImgSelFiv_clicked()));
    }
    

    There are also six slot functions as you can see above that are called when each button is pressed. All they do is call the "fnImgSelBtnTextSwitch" function while passing the number of the button that was pressed. So as you can see the function needs to be able to read the text off the button and change the text of the button. I changed the function to have the "MainWindow::" class identification in front of it as well as putting the function under "private" in mainwindow.h in hopes that this would keep the function in the class and that the button reference would be recognized.

    However, I get these errors:

    Errors.png

    The first error occurs on the first line of the "fnImgSelBtnTextSwitch" I showed earlier, which seems like the root of the problem. I am forgetting some kind of syntax to put the buttons I create into the class for use with other class functions or something of that sort, I am just not sure exactly what I am doing wrong. I am pretty sure that the function itself is fine it just can't see the QPushButton array I made because I am not setting it up properly for use in that function.

    I had wanted to avoid actually having to have the pointer be an input to the function so is there another way such as keeping the function in class (like I attempted to do above)? Or is that the only way?



  • @oblivioncth

    As it seems your QPushButton Array is declared in another function it is not visible inside the void MainWindow::fnImgSelBtnTextSwitch(int BtnNum); function.

    If you declare it as a variable in the class definition, you can access it from all methods inside your class.

    for example:

    //mainwindow.h
    class MainWindow {
    
    //...
      QPushButton * PshBtnImgSel[];
    //...
    }
    

    Then you can initalize the Array something like this:

     PshBtnImgSel[] = {new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add"),new QPushButton("Add")};
    

    and access it in void MainWindow::fnImgSelBtnTextSwitch(int BtnNum)



  • @the_

    Haha, you guys keep offering solutions that aren't really specific to Qt and more so general C++ convention. Sorry for my lack of knowledge on some of the more basic things. Since I'm not that experiended with class I keep forgetting you can do things like that.

    Thanks, I'll give it a shot.

    EDIT:

    Worked perfectly. Thank you. Though, now I am having an in issue in the compare strings statement. The Qstring compare function doesn't seem to work the same way that strcmp for std does.


  • Qt Champions 2016

    @oblivioncth

    • The Qstring compare function doesn't seem to work the same way that strcmp for std does.

    Hi
    http://en.cppreference.com/w/cpp/string/byte/strcmp
    returns zero when match found and
    http://doc.qt.io/qt-5/qstring.html#compare-5
    also return zero when matched so not sure
    what difference you mean?

    so if you mean equal here
    if(QString::compare("Add",PshBtnImgSel[BtnNum]->text()))
    I would expect
    if( 0 == QString::compare("Add",PshBtnImgSel[BtnNum]->text()))



  • @mrjj

    Huh, I swore that strcmp returned a 1 when there was a match, but then I guess I'd be wrong.

    Thank you for pointing that out, I mind as well just get a Noob tattoo haha, I've forgotten a lot more C++ than I thought. Additionally, I ended up just using the "==" operator after learning that it works for QStrings. I thought it didn't work for regular strings, though upon researching that it appears it does, which confuses me because I remember always using strcmp because I remembered the "==" operator not working for strings. Maybe I was using something was wrong with my compiler lol. Oh well.


  • Qt Champions 2016

    @oblivioncth
    Well to be fair, i guess most programmers at some point,
    had this feeling that a match would return true.
    also while correct, it reads all wrong
    if ( ! strcmp(x,y) )

    Well the == is an operator and a class can define one to allow to compare this way
    inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
    so for many string classes == can be used.
    Using == with char *, sometimes/often leads to unexpected results and maybe
    you remember those cases?



  • @mrjj

    Perhaps that was the case. Thank you for your help and friendliness :)


  • Qt Champions 2016

    @oblivioncth
    you are very welcome. :)

    One note since I saw u are using mutiple slots ( which is fine) but just so u know
    inside a slot, you can use the
    sender() function
    get the object that send the signal.
    its a generic base class pointer but you can cast it to get the real object.

    like in your case
    QPushButton* button = qobject_cast<QPushButton*>(sender());
    if(button) { // check if non null, means cast worked
    do the text stuff
    }

    So that way you could just have slot for all buttons if all text swapping is the same.
    With the cast, the button variable would point to the button and u dont need to say
    ui->buttonX

    Nothing wrong with your code, just a note as this can be handy sometimes.



  • @mrjj That promotes organization and conciseness which I like, thank you for the tip!



  • @mrjj
    (Since you are a Mod I applogize if this is breaking some kind of double post rule or an issue over becoming off topic)

    Also, perhaps I should make a new topic, but there is one other thing I am stuck on now. I currently have a QTableWidget in my UI with three columns. I want the first two to be fixed in width, and all three to not be changeable by the user. The third column's width I want to be a little picky about though. I want it so that it can dynamically change size so that if text is put into it that is too large it will expand with a scroll bar instead of adding a second line to the cell, which I have accomplished with:

     ui->imgTable->horizontalHeader()->setSectionResizeMode(2,QHeaderView::ResizeToContents);
    

    But the other condition is that I want it to always at least fill up the rest of the tables space so that there isn't a blank white area to the right of the column. Because if there is nothing in the cell and the above mode is applied the column width shrinks to the size of the header name "File Path" which is too small to take up the rest of the tables width. This normally can be accomplished by using:

    ui->imgTable->horizontalHeader()->setSectionResizeMode(2,QHeaderView::Stretch)
    

    but that cancels out the previous setting and makes it so that if the text gets too big a 2nd line in the cell is added (which I do not want) instead of the table gaining a scroll bar (which is what I want). So I figured, OK, all I need to do is leave it on "ResizeToContents" and then just set a minimum size so that it will dynamically resize the column but never become so small that it wont take up the whole table region. The problem I have is two fold.

    First, the closest command I could find to do this is:

    ui->imgTable->horizontalHeader()->setMinimumSectionSize(150)
    

    but the problem is there is no index argument so it seems that it will set a minimum size for all columns instead of just column 2. I cannot seem to find a solution that only affects one column.

    Second, even if I can set a minimum size, the size is always an integer so I probably cannot get the minimum size set perfectly so that there is no visible space between the last column and the table right extent. Therfore it would be optimal if I could somehow have a "ReizeToContents"/"Stretch" hybrid that made it stretch to the table extent but resize dynamically only if the text gets too big for the cell. Basically, "Stretch" by default, but becomes "ResizeToConetents" if the cell text length hit the limit of the cell size. It doesn't look like this can be done however. I can think of a way to cause this behavior but it would be ugly:

    1. Check the length of the cell text everytime a change is made (there is only one way for it to change so this isn't too hard)
    2. Since the size of the table is fixed I can determine how many characters it would take to go over the cell size, which would normally cause a 2nd line to be added
    3. Have the behavior default to stretch but change to ResizeToContents if the length of the text in the cell gets large enough that it would create a 2nd line.

    The above should give the exact behavior I am looking for but I would prefer to not having to resort to that as it is ugly, feels inefficient, and seems unnecessary for something that looks like it could take 1-3 lines if I could find the right functions.

    Does Qt have something to support this behavior or will I have to implement my awkward solution? I can take pictures if some of my explanations are unclear.



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.