PDA

View Full Version : best layout fo a fixed part (on the left) and a dynamic part (on the right)



mcanonic
15th May 2019, 16:46
HI,
I'm new in pyqt5 and I'm going to realize a GUI where on the left I have 3 buttons and on the right I could have different widgets depending on which button has been pushed.
Scenario looks like this

BUTTON 1 |
BUTTON 2 | DYNAMIC AREA
BUTTON 3 |

In this scenario, where the left part is fixed (I'll always have the 3 buttons) and on the right I will have different widgets, which is the best layout to use?

The grid layout?

Thanks,
M

anda_skoa
15th May 2019, 20:59
Maybe QVBoxLayout on the left side, a QStackWidget on the right side.

Cheers,
_

mcanonic
16th May 2019, 10:47
Ok, thank you.
If I'm not wrong, I need a parent layout that in my case could be a QHBoxLayout, which will contain a QVBoxLayout (the lest panel) and a QStackWIdget (on the right panel). Maybe I'm wrong since the QStackWidget is not a layout.
Two more questions.
Any time a button is click on the left panel, I have to remove all widgets in the right panel. Should I looking for any widget in the right panel and delete it one-by-one?
May I add a vertical line between the left panel and the right one?
Thanks for your support and patience.
M

Ginsengelf
16th May 2019, 13:51
Hi, the QStackWidget has multiple pages, but it displays only one of these pages at a time. So instead of deleting any widgets on your right panel, you arrange your widgets on different pages (one for each button) and simply change the page of the QStackWidget when a button is clicked.

To draw a line use QFrame with QFrame::VLine as shape, or try a QSplitter.

Ginsengelf

anda_skoa
17th May 2019, 15:47
If I'm not wrong, I need a parent layout that in my case could be a QHBoxLayout, which will contain a QVBoxLayout (the lest panel) and a QStackWIdget (on the right panel).

Right. Or a QSplitter as Ginsengelf said.



Any time a button is click on the left panel, I have to remove all widgets in the right panel. Should I looking for any widget in the right panel and delete it one-by-one?
May I add a vertical line between the left panel and the right one?


Again as Ginsengelf said, the idea about using a QStackWidget is to avoid having to do that.

Essentially you add your buttons to a QButtonGroup and use the "id" argument to associate the page index on the stack widget side with the button.
You can then directly connect the buttongroup's buttonClicked(int) signal with the stackedwidget's setCurrentIndex(int) slot and are done.

Cheers,
_

mcanonic
30th May 2019, 18:47
Right. Or a QSplitter as Ginsengelf said.



Again as Ginsengelf said, the idea about using a QStackWidget is to avoid having to do that.

Essentially you add your buttons to a QButtonGroup and use the "id" argument to associate the page index on the stack widget side with the button.
You can then directly connect the buttongroup's buttonClicked(int) signal with the stackedwidget's setCurrentIndex(int) slot and are done.

Cheers,
_

Dear Anda_Skoa,
your suggestions seems very interesting and I tried to implement it with no luck. For now, I just skiped the QButtonGroup suggestion and I was focusing in the QStackWidget. So, in the main screen I have a left panel with a QVBoxLayout and on the right panel I have a QStackedWidget:



def UI(self):
mainScreen=QHBoxLayout()

leftPanel = QVBoxLayout()
#rightPanel = QVBoxLayout()
rightPanel = QStackedWidget()

line = QtWidgets.QFrame()
line.setGeometry(QtCore.QRect(213, 20, 20, 391))
line.setFrameShape(QtWidgets.QFrame.VLine)
line.setFrameShadow(QtWidgets.QFrame.Sunken)
line.setObjectName("line")

self.loadLeftPanel(leftPanel)
self.loadRightPanel(rightPanel)

mainScreen.addLayout(leftPanel)
mainScreen.addWidget(line)
mainScreen.addLayout(rightPanel)


rightPanel.setCurrentIndex(1)

self.setLayout(mainScreen)
self.show()


Now the problem is in the "loadRightPanel" function. Looking at some example I have to add some widget (one for each page), provide a function for the UI specific for each stack and finally add the widget at the layout. This is what I did:


def loadRightPanel(rightPanel):
stack1 =QWidget()
stack2 =QWidget()
#stack3 =QWidget()

stack1UI(stack1)
stack2UI(stack2)
#stack3UI()

rightPanel.addWidget(stack1)
rightPanel.addWidget(stack2)
#rightPanel.addWidget(stack3)


and the UI for each stack looks like this:


def stack1UI(self,stack1):
layout1 = QVBoxLayout()
welcomeText1 = QLabel("STACK1 - Android Forensic Automator (AnForA)", self)
welcomeText1.setAlignment(Qt.AlignCenter)
welcomeText1.setFont(QtGui.QFont("Times", 12, QtGui.QFont.Bold))
welcomeText2 = QLabel("a software tool that automates the forensic analysis of Android applications", self)
welcomeText2.setFont(QtGui.QFont("Times", 12))
layout1.addStretch(1)
layout1.addWidget(welcomeText1)
layout1.addWidget(welcomeText2)
layout1.addStretch(1)
stack1.setLayout(layout1)


but something is wrong and I'm little confuse.
Could you help me out, please?
M

anda_skoa
31st May 2019, 09:26
The only thing that looks outright wrong is this line



mainScreen.addLayout(rightPanel)

rightPanel is a QStackedWidget so it needs to be added with addWidget()


mainScreen.addWidget(rightPanel)


I would also recommend to do the stack pages as separate classes, makes the code much more maintainable



class WelcomePage(QWidget)


....


stack1 = WelcomePage()
rightPanel.addWidget(stack1)


Cheers,
_

mcanonic
31st May 2019, 17:59
Thank you Ada, your suggestion are precious.
Now I have a class for each page as you suggested.

I'm missing one last step to ask, if you (or/and other in this forum) have more patience.

The leftPanel has buttons and by click on some button, I have to chage the page on rightPanel.

So when I load the left panel, I need a button able to use rightPanel.setCurrentIndex(1).

The code is like this:


def loadLeftPanel(self,leftPanel,rightPanel):
...
installAPKBtn = QPushButton("Install APK on the device", self)
...
leftPanel.addWidget(installAPKBtn)

installAPKBtn.clicked.connect(self.installAPK())

def installAPK(self):
self.rightPanel.setCurrentIndex(1)


I think I need something like: installAPKBtn.clicked.connect(self.installAPK(righ tPanel))
but my various attempt did not work

Any idea?

Thanks,
M

Added after 8 minutes:

In the meanwhile, I read about lambda:

installAPKBtn.clicked.connect(lambda:self.installA PK(rightPanel))

def installAPK(self, rightPanel):
rightPanel.setCurrentIndex(1)

is this the right way to manage these problem?

Thanks again,
M

anda_skoa
1st June 2019, 10:17
So when I load the left panel, I need a button able to use rightPanel.setCurrentIndex(1).

Right, or using a QButtonGroup



I think I need something like: installAPKBtn.clicked.connect(self.installAPK(righ tPanel))

Not necessarily. Your code should work fine if self.rightPanel exists, i.e. if rightPanel is a member variable of your class.



In the meanwhile, I read about lambda:

installAPKBtn.clicked.connect(lambda:self.installA PK(rightPanel))

def installAPK(self, rightPanel):
rightPanel.setCurrentIndex(1)

is this the right way to manage these problem?


Yes, this is also an option. The lambda can even call setCurrentIndex directly.

Cheers,
_

mcanonic
3rd June 2019, 16:56
Thank you! I'm almost done.
This should be my last question.

As you suggested, I wrote the code with QButtonGroup like this:


def loadLeftPanel(self,leftPanel,rightPanel):
#Create QButtonGroup
btnGrp = QButtonGroup(self)
btnGrp.setExclusive(True)
btnGrp.addButton(installAPKBtn,1)
btnGrp.addButton(detectPathBtn,2)
btnGrp.addButton(startExpBtn,3)
btnGrp.addButton(analyzeResBtn,4)
btnGrp.addButton(quitBtn,5)


btnGrp.buttonClicked.connect(self.onClick)

def onClick(self,btn):
rightPanel.setCurrentIndex(id)


Now the problem is this: onClick has the object relative to the button that was pressed, but I need to know the id not the button object. As you can see, I add the button by specify as second parameter the id, but I'm not able to retreive it.

Any idea how can I fix this?

Thanks,
M

Added after 1 41 minutes:


Thank you! I'm almost done.
This should be my last question.

As you suggested, I wrote the code with QButtonGroup like this:


def loadLeftPanel(self,leftPanel,rightPanel):
#Create QButtonGroup
btnGrp = QButtonGroup(self)
btnGrp.setExclusive(True)
btnGrp.addButton(installAPKBtn,1)
btnGrp.addButton(detectPathBtn,2)
btnGrp.addButton(startExpBtn,3)
btnGrp.addButton(analyzeResBtn,4)
btnGrp.addButton(quitBtn,5)


btnGrp.buttonClicked.connect(self.onClick)

def onClick(self,btn):
rightPanel.setCurrentIndex(id)


Now the problem is this: onClick has the object relative to the button that was pressed, but I need to know the id not the button object. As you can see, I add the button by specify as second parameter the id, but I'm not able to retreive it.

Any idea how can I fix this?

Thanks,
M

In the meanwhile, I've put btnGrp as a global value (I know this is bad, but I just want to see how it works) and I have modified the onClick method like this:


def onClick(self,btn):
print('Text of the pressed btn '+btn.text())
print('id of the pressed btn '+str(btnGrp.checkedId()))


The text is correct but not the id: it is alway equals to -1.

I cannot see where my code is wrong.
Thanks for your help.
M

anda_skoa
3rd June 2019, 20:51
Now the problem is this: onClick has the object relative to the button that was pressed, but I need to know the id not the button object. As you can see, I add the button by specify as second parameter the id, but I'm not able to retreive it.


The problem is that there are two buttonClicked() signals, one with a button and one with an ID as the argument.

Try this


btnGrp.buttonClicked[int].connect(self.onClick)


Cheers,
_