Results 1 to 11 of 11

Thread: Circular dependency fix/alternative?

  1. #1
    Join Date
    Oct 2015
    Posts
    28
    Thanks
    10
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Post Circular dependency fix/alternative?

    Hi,

    I have this problem with my Qt application that I was hoping one of you could help me with.
    In this application I have the classes ColorSelect and DeviceDialog, each with its own ui. But I'm getting errors because I'm trying to call functions from each other.
    I know that this isn't allowed because of a thing called circular dependency, so I was wondering what I can do instead that is allowed/won't give me errors?
    (I have included devicedialog.h in colorselect.h and vice versa)

    in my colorselect.cpp:

    void ColorSelect::on_listWidget_itemDoubleClicked(QList WidgetItem *item)
    {
    //launches the device dialog where data is entered in line edits
    DeviceDialog dialog;
    dialog.setLineEdits(item->data(Qt::UserRole).toString());
    dialog.setModal(true);
    dialog.exec();
    }

    in my devicedialog.cpp:

    void DeviceDialog::on_buttonBox_accepted()
    {
    //sets listWidgetItem data (found in colorselect.ui) with text from line edits (found in devicedialog.ui)
    colorSelect->setItemData(ui->houseLineEdit->text(), ui->unitLineEdit->text());
    }

    //Leutzig

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Circular dependency fix/alternative?

    (I have included devicedialog.h in colorselect.h and vice versa
    Why? Do you have code in the header files that needs to know the signatures for the methods declared in either of these two classes? Or does each class simply need to know that there are classes by the names of ColorSelect and DeviceDialog?

    In other words, if the ColorSelect header file only contains something like a pointer or reference to the DeviceDialog, then all you need is to place the statement (called a "forward declaration") before the other class' name is needed:

    Qt Code:
    1. // ColorSelect .h
    2.  
    3. class DeviceDialog; // forward declaration
    4.  
    5. class ColorSelect : public QWidget
    6. {
    7. Q_OBJECT
    8.  
    9. public:
    10. ColorSelect( QWidget * parent );
    11.  
    12. //...
    13.  
    14. private:
    15. DeviceDialog * devDlg;
    16. }
    To copy to clipboard, switch view to plain text mode 

    and the same for DeviceDialog.h. The only place you need to include the header files themselves is in the .cpp files where you need to know the definitions of the classes.

    If the only place you reference the name of the other class is to define a variable (as in the example above), you don't need the forward declaration either. You can simply write "class DeviceDialog * devDlg;", which tells the compiler you're going to store a pointer to a class named "DeviceDialog" in that variable.

  3. The following user says thank you to d_stranz for this useful post:

    Leutzig (30th November 2015)

  4. #3
    Join Date
    Oct 2015
    Posts
    28
    Thanks
    10
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Circular dependency fix/alternative?

    For some reason I get the errors:

    error: member access into incomplete type 'class DeviceDialog'
    dialog->setLineEdits(item->data(Qt::UserRole).toString());
    ^

    forward declaration of 'DeviceDialog'
    class DeviceDialog *dialog;
    ^

    ... And a few more of those from the other functions. Also the functions I'm trying to call are no longer "recognised" in the cpp's.

    ColorSelect.h:

    class ColorSelect : public QWidget
    {
    Q_OBJECT

    public:
    explicit ColorSelect(QWidget *parent = 0)
    void setItemData(QString, QString);
    ~ColorSelect();

    private slots:
    void on_listWidget_itemDoubleClicked(QListWidgetItem *item);

    private:
    Ui::ColorSelect *ui;
    class DeviceDialog *dialog;
    };

    DeviceDialog.h:

    class DeviceDialog : public QDialog
    {
    Q_OBJECT

    public:
    explicit DeviceDialog(QWidget *parent = 0);
    void setLineEdits(QString);
    ~DeviceDialog();

    private slots:
    void on_buttonBox_accepted();

    private:
    class ColorSelect *colorSelect;
    Ui::DeviceDialog *ui;
    };

  5. #4
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Circular dependency fix/alternative?

    Did you #include the header files in the .cpp files? Those compiler complaints indicate that you didn't. The forward declarations simply tell the compiler that classes of those names exist. If you want to use one of the classes, you have to include the header file so the compiler can find the class definitions.

    If your compiler doesn't like the "class XYZ * pClass;" style, use the forward declaration version (at the top of the .h file, before the other class' definition.

    Please use "CODE" tags when posting source code. When making a post, click "Go Advanced". On the toolbar, click the "#" icon. This will insert a pair of open/close CODE tags. Paste your code between them.

  6. #5
    Join Date
    Oct 2015
    Posts
    28
    Thanks
    10
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Circular dependency fix/alternative?

    Yes, that was it. I didn't realise that both headers should be included in both cpp files. But I'm encountering a problem when using the pointer to the DeviceDialog object in the function on_listWidget_itemDoubleClicked from colorselect. The application crashes when write it like this:

    Qt Code:
    1. void ColorSelect::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
    2. {
    3. dialog->setLineEdits(item->data(Qt::UserRole).toString());
    4. dialog->setModal(true);
    5. dialog->exec();
    6. }
    To copy to clipboard, switch view to plain text mode 

    But this works:

    Qt Code:
    1. void ColorSelect::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
    2. {
    3. DeviceDialog dialog;
    4. dialog.setLineEdits(item->data(Qt::UserRole).toString());
    5. dialog.setModal(true);
    6. dialog.exec();
    7. }
    To copy to clipboard, switch view to plain text mode 

    And for some odd reason my application also crashes in another function from colorselect:

    Qt Code:
    1. void ColorSelect::setItemData(QString house, QString unit)
    2. {
    3. ui->listWidget->currentItem()->setData(Qt::UserRole, house + unit);
    4. }
    To copy to clipboard, switch view to plain text mode 

    And in this one I've tried doing other things with ui pointer but it crashes whenever it reaches a line like "ui->something...", even though it's just a simple public function in my colorselect class. All my other ColorSelect functions can do this. So it seems like I'm having some trouble with private pointers to objects in my ColorSelect class.

  7. #6
    Join Date
    Jan 2012
    Location
    Dortmund, Germany
    Posts
    159
    Thanks
    69
    Thanked 10 Times in 8 Posts
    Qt products
    Qt4
    Platforms
    Windows Android

    Default Re: Circular dependency fix/alternative?

    You write, that your
    application crashes when write it like this
    I'd think that's quite normal - I don't see anything actually creating the dialog object, you are just using a pointer (hint: if it uses-> it is a pointer) to a random place in your memory.
    Before line 3 in your 1st example, create an object for that pointer:
    Qt Code:
    1. dialog = new DeviceDialog (this);
    To copy to clipboard, switch view to plain text mode 
    But beware! If you allocate space that way, you MUST clean up and free the ressource afterwards, so after line 5 you should add
    Qt Code:
    1. delete dialog;
    To copy to clipboard, switch view to plain text mode 
    After that, never use the pointer again, until it is created anew.

    In your second example, you create a dialog object (you dont use the pointer that is a member of your class). This "lives" only in the very scope it is created in, c++ deletes it automatically when you leave the method.

    As for your third example: is it possible that this method is called earlier in time than the other methods (that are working)? If ui is not yet created when you use it, any use of it will crash your application.
    HTH,
    Sebastian

  8. The following user says thank you to sedi for this useful post:

    Leutzig (30th November 2015)

  9. #7
    Join Date
    Oct 2015
    Posts
    28
    Thanks
    10
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Circular dependency fix/alternative?

    Thanks Sebastian, I'll be reading up on dynamic allocation. I'm quite certain that I don't call the method from third example before ui is created. It is called when I press the OK button in the DeviceDialog dialog which also closes the dialog. I'm not sure if that could cause the problem since you can't really interact with your main window when the dialog is open (at least not in my application).

    Qt Code:
    1. void DeviceDialog::on_buttonBox_accepted()
    2. {
    3. colorSelect = new ColorSelect(this);
    4. //When OK is pressed, set item data with house code and unit code
    5. colorSelect->setItemData(ui->houseLineEdit->text(), ui->unitLineEdit->text());
    6. delete colorSelect;
    7. }
    To copy to clipboard, switch view to plain text mode 

  10. #8
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Circular dependency fix/alternative?

    Qt Code:
    1. void DeviceDialog::on_buttonBox_accepted()
    2. {
    3. colorSelect = new ColorSelect(this);
    4. //When OK is pressed, set item data with house code and unit code
    5. colorSelect->setItemData(ui->houseLineEdit->text(), ui->unitLineEdit->text());
    6. delete colorSelect;
    7. }
    To copy to clipboard, switch view to plain text mode 

    No, no, no, you do not want this. This is equivalent to a NO-OP - you create the class instance on the stack, do something with it, and it immediately gets deleted along with whatever you did.

    Unfortunately, you cannot create an instance of ColorSelect in the DeviceDialog constructor if you also create an instance of DeviceDialog in the ColorSelect constructor. That sets up an infinite recursion: ColorSelect creates DeviceDialog which creates ColorSelect which creates DeviceDialog ad infinitum until your stack blows up.

    BUT I am not at all sure that this code does what you intend in your app. Even if you work around the recursion problem, you will still end up with independent instances of each class, instead of one instance of ColorSelect and one instance of DeviceDialog. Changes made to one instance of either class will be local to that instance only, so for example changing something in the list widget of one DeviceDialog won't have any effect on the list widget in other instances.

    Maybe you can show some code on how you create and use these classes and we can give you some advice on how to structure the dependencies between them.

  11. The following user says thank you to d_stranz for this useful post:

    sedi (2nd December 2015)

  12. #9
    Join Date
    Oct 2015
    Posts
    28
    Thanks
    10
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Circular dependency fix/alternative?

    Yes I see what you mean. I'm not sure how to get around this recursion problem. I'll greatly appreciate if you could take a look at my ColorSelect class and DeviceDialog class and let me know how to structure these dependencies.

    Here's a link to the project folder:
    https://mega.nz/#!Rx4FkYyI

    Decryption key for download:
    !b3qx9qGK5hKq1QqTFapsDRE6bF1om3uoTk3luq6N7r8

    It might be easier to follow if you get the whole cpp and header files instead of the snips and pieces of the code above.

  13. #10
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Circular dependency fix/alternative?

    Wow, no offense, but that's a way more confusing implementation that it needs to be. No wonder you are having trouble with circular dependencies.

    I think most of your problem stems from the fact that there is no one "in charge" in your GUI. You have a tabbed dialog (a QWidget, actually - ColorSelect) that contains two tabs, neither of which knows about the other but which need to communicate information. And probably, because you couldn't figure out how to do it in Qt Designer, you create and show() your ColorWheel instance separately.

    I am going to be presumptuous and refactor your code a bit to make it less interdependent.

    First step is to make ColorWheel a proper and independent child of the ColorSelect widget, by "promoting" the QWidget you have created in Designer. To do this, you open the ui file in Designer, right-click on the place where you inserted the QWidget instead of the ColorWheel, and select "Promote to...". This will open the widget promotion dialog. In the Promoted class name box, type "ColorWheel" and it will automatically insert the header file name in the box below. Click "Add", then click Promote and Close. If you inspect the object list, you will now see that the object type for the color wheel is now ColorWheel instead of QWidget.

    Next step is to get rid of the dependence of ColorWheel on ColorSelect. Remove the ColorSelect argument from the constructor, remove the member variable, and remove all references to it from the ColorWheel .cpp and .h files. Remove the include of colorselect.h. Replace the call to colorSelect->updatePalette() in the ColorWheel class with a call to emit colorChange(). Move the QPalette stuff out of main() and into the ColorWheel constructor:

    Qt Code:
    1. ColorWheel::ColorWheel(QWidget *parent) :
    2. QWidget(parent),
    3. initSize(200,200),
    4. mouseDown(false),
    5. margin(0),
    6. wheelWidth(30),
    7. current(Qt::red),
    8. inWheel(false),
    9. inSquare(false)
    10. {
    11. // resize(initSize);
    12. current = current.toHsv();
    13. // setMinimumSize(200,200);
    14. setCursor(Qt::CrossCursor);
    15.  
    16. QPalette wheelPalette = palette();
    17. wheelPalette.setBrush(QPalette::Window, QBrush(Qt::white));
    18. setPalette(wheelPalette);
    19.  
    20. }
    To copy to clipboard, switch view to plain text mode 

    Now, ColorWheel is completely independent of ColorSelect. You need to reconnect the notification about color changes in the wheel so that ColorSelect can follow them. In the ColorSelect constructor, add a connect statement:

    Qt Code:
    1. connect( ui->colorWheelWidget, SIGNAL( colorChange( const QColor & ) ), this, SLOT( updatePalette( const QColor & ) ) );
    To copy to clipboard, switch view to plain text mode 

    and rewrite the updatePalette() method as a slot:

    Qt Code:
    1. // .h:
    2.  
    3. private slots:
    4. void updatePalette( const QColor & );
    5.  
    6. // .cpp:
    7.  
    8. void ColorSelect::updatePalette( const QColor & color )
    9. {
    10. //get palette
    11. QPalette pal = ui->colorPalette->palette();
    12.  
    13. //update palette with colorToRGB
    14. pal.setColor(QPalette::Window, color);
    15.  
    16. ui->colorPalette->setPalette(pal);
    17. emit colorChanged(color);
    18. qDebug()<< color;
    19. }
    To copy to clipboard, switch view to plain text mode 

    Next, now that you are building the ColorWheel entirely within ColorSelect, you can dramatically simplify main():

    Qt Code:
    1. #include "colorselect.h"
    2. #include "serialcom.h"
    3.  
    4. int main(int argc, char *argv[])
    5. {
    6. QApplication a(argc, argv);
    7. SerialCom *serialCom = new SerialCom();
    8. ColorSelect *colorSelect = new ColorSelect(0,serialCom);
    9. colorSelect->setWindowTitle("RGB-LED Color Picker");
    10.  
    11. //show UI
    12. colorSelect->show();
    13.  
    14. return a.exec();
    15. }
    To copy to clipboard, switch view to plain text mode 

    Next step is to break the dependence of DeviceDialog on ColorSelect. DeviceDialog's entire purpose is to capture two pieces of text from the user. So, it has no business knowing that ColorSelect is going to add those things to a list widget somewhere. It just simply needs to announce to the world, "Hey, I have new text!" So, DeviceDialog becomes this:

    Qt Code:
    1. // .h:
    2. #ifndef DEVICEDIALOG_H
    3. #define DEVICEDIALOG_H
    4.  
    5. #include <QDialog>
    6.  
    7. namespace Ui {
    8. class DeviceDialog;
    9. }
    10.  
    11. class DeviceDialog : public QDialog
    12. {
    13. Q_OBJECT
    14.  
    15. public:
    16. explicit DeviceDialog(QWidget *parent = 0);
    17. void setLineEdits(QString);
    18. ~DeviceDialog();
    19.  
    20. signals:
    21. void houseAndUnitChanged( const QString & house, const QString & unit );
    22.  
    23. private slots:
    24. void on_buttonBox_accepted();
    25.  
    26. private:
    27. Ui::DeviceDialog *ui;
    28. };
    29.  
    30. #endif // DEVICEDIALOG_H
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // .cpp
    2.  
    3. #include "devicedialog.h"
    4. #include "ui_devicedialog.h"
    5.  
    6. DeviceDialog::DeviceDialog(QWidget *parent) :
    7. QDialog(parent),
    8. ui(new Ui::DeviceDialog)
    9. {
    10. ui->setupUi(this);
    11. }
    12.  
    13. DeviceDialog::~DeviceDialog()
    14. {
    15. delete ui;
    16. }
    17.  
    18. void DeviceDialog::setLineEdits(QString houseAndUnit)
    19. {
    20. ui->houseLineEdit->setText(houseAndUnit.mid(0,1));
    21. ui->unitLineEdit->setText(houseAndUnit.mid(1,1));
    22. }
    23.  
    24. void DeviceDialog::on_buttonBox_accepted()
    25. {
    26. emit houseAndUnitChanged( (ui->houseLineEdit->text(), ui->unitLineEdit->text() );
    27. }
    To copy to clipboard, switch view to plain text mode 

    Finally, you have to fix up ColorSelect so it handles the DeviceDialog signal:

    Qt Code:
    1. // .h
    2.  
    3. private slots:
    4. void onHouseAndUnitChanged( const QString & house, const QString & unit );
    5.  
    6. // and delete the "DeviceDialog" member variable.
    7.  
    8. // .cpp
    9.  
    10. void ColorSelect::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
    11. {
    12. DeviceDialog dialog(this);
    13.  
    14. connect( &dialog, SIGNAL( houseAndUnitChanged( const QString &, const QString & ) ), this, SLOT( onHouseAndUnitChanged( const QString &, const QString & ) ) );
    15. dialog.setLineEdits(item->data(Qt::UserRole).toString());
    16. dialog.exec();
    17. }
    18.  
    19. void ColorSelect::onHouseAndUnitChanged( const QString & house, const QString & unit )
    20. {
    21. setItemData( house, unit );
    22. }
    To copy to clipboard, switch view to plain text mode 


    There's still more cleanup and refactoring you could do, but now you have something where there are no circular dependencies, and in fact very few dependencies at all.

    I've attached a zip file with all the code changes.

    colorselect_serial.zip

    By the way, I am out of the office until the end of the week, so I won't be following the forum.

  14. The following user says thank you to d_stranz for this useful post:

    Leutzig (1st December 2015)

  15. #11
    Join Date
    Oct 2015
    Posts
    28
    Thanks
    10
    Qt products
    Qt5
    Platforms
    MacOS X Windows

    Default Re: Circular dependency fix/alternative?

    This is just... Absolutely great work, thank you so much, d_stranz. I obviously didn't know to use signals and slots, but I see why it makes so much more sense using them. Hopefully your eyes are alright after looking at that mess, as it is my first actual Qt application. But man... Thank you.

    //Leutzig

Similar Threads

  1. Circular Layout
    By Leolander in forum Qt Programming
    Replies: 1
    Last Post: 30th March 2010, 08:02
  2. Circular QLinkedList
    By dyngoman in forum Qt Programming
    Replies: 1
    Last Post: 24th March 2010, 08:57
  3. circular buttons
    By moabi in forum Newbie
    Replies: 2
    Last Post: 18th March 2010, 04:28
  4. Circular Linklist.
    By AmolShinde_8 in forum Qt Programming
    Replies: 2
    Last Post: 31st October 2008, 04:23
  5. Circular scale
    By fruzzo in forum Qwt
    Replies: 1
    Last Post: 6th March 2008, 07:20

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.