it also gave me an error about how the variable (tab) was not used
A warning, not an error. This is an important difference! Unlike errors, one can ignore and even disable warnings (even though one probably shouldn't... ;)). That specific warning exists to warn you about the fact that you defined a function expecting an argument but never actually use that argument inside. So either that requirement shouldn't be there in the first place or you forgot to use the argument!
Now to your actual problem: TabContainer and Tabs are two distinct (!) ways to achieve tabs in your game. You don't need one for the other! A TabContainer will render every Control-child as a Tab. So a tree like this will work as well:
More so: it will work out of the box without the need to connect any signals. If you click on a tab, the Control-node associated with it will become visible. Simple as that! That being said, you can connect the TabContainers
tab_selected-signal to enhance that default behavior. Check this out - there won't be a warning this time:
print("clicked on tab number ", tab)
So no matter which tab you select, it will always run the same callback. In order to get different behavior based on which tab was selected, you need to check the argument.
The most confusing part here is that using Tabs as children of a TabContainer will work - they are Control-nodes after all. However their
tab_clicked-signal will be ignored, even if one connects it to a callback. Not a bug, but still very confusing.