How to have two widgets in one Main window

孤街浪徒 提交于 2021-02-11 12:32:24

问题


I am trying the whole morning already to fix that. So I have a PyQt Main Window where I want to display two widgets. In the first widget there are articles listed (which works so far). When I click on them until now a QMessageBox is opening, but I want that a second widget is opening where I can read the RSS Feed. But this is not working. See Code below:

class ArticleWidgets(QWidget):
    def __init__(self, *args):
        super().__init__(*args)

        self.setGeometry(610, 610, 600, 600)

        self.initUi()

    def initUi(self):
        self.box = QHBoxLayout(self)

    def show(self, feed=None):

        self.title = QLabel()
        self.summary = QLabel()
        self.link = QLabel()

        if feed:
            self.title.setText(feed[0])
            self.summary.setText(feed[1])
            self.link.setText(feed[2])

        self.box.addWidget(self.title)
        self.box.addWidget(self.summary)
        self.box.addWidget(self.link)

        self.setLayout(self.box)


class TitleWidgets(QWidget):
    def __init__(self, *args):
        super().__init__(*args)

        self.setGeometry(10, 10, 600, 600)

        self.initUi()

    def initUi(self):
        vbox = QHBoxLayout(self)

        self.titleList = QListWidget()
        self.titleList.itemDoubleClicked.connect(self.onClicked)
        self.titleList.setGeometry(0, 0, 400, 400)
        self.news = ANFFeed()
        for item in self.news.all_feeds:
            self.titleList.addItem(item[0])
        vbox.addWidget(self.titleList)

    def onClicked(self, item):
        feeds = self.news.all_feeds
        id = 0
        for elem in range(len(feeds)):
            if feeds[elem][0] == item.text():
                id = elem

        summary = feeds[id][1] + '\n\n'
        link = feeds[id][2]

        if feeds and id:
            #ANFApp(self).show_articles(feeds[id])
            show = ANFApp()
            show.show_articles(feed=feeds[id])

        QMessageBox.information(self, 'Details', summary + link)


class ANFApp(QMainWindow):
    def __init__(self, *args):
        super().__init__(*args)

        self.setWindowState(Qt.WindowMaximized)
        self.setWindowIcon(QIcon('anf.png'))
        self.setAutoFillBackground(True)

        self.anfInit()

        self.show()

    def anfInit(self):
        self.setWindowTitle('ANF RSS Reader')

        TitleWidgets(self)
        #article_box = ArticleWidgets(self)

        exitBtn = QPushButton(self)
        exitBtn.setGeometry(600, 600, 100, 50)
        exitBtn.setText('Exit')
        exitBtn.setStyleSheet("background-color: red")
        exitBtn.clicked.connect(self.exit)

    def show_articles(self, feed=None):
        present = ArticleWidgets()
        present.show(feed)

    def exit(self):
        QCoreApplication.instance().quit()

回答1:


Solution using Pyqtgraph's Docks and QTextBrowser

Here is a code trying to reproduce your sketch. I used the Pyqtgraph module (Documentation here: Pyqtgraph's Documentation and Pyqtgraph's Web Page) because its Dock widget is easier to use and implement from my perspective.

You must install the pyqtgraph module before trying this code:

import sys
from PyQt5 import QtGui, QtCore
from pyqtgraph.dockarea import *

class DockArea(DockArea):
    ## This is to prevent the Dock from being resized to te point of disappear
    def makeContainer(self, typ):
        new = super(DockArea, self).makeContainer(typ)
        new.setChildrenCollapsible(False)
        return new

class MyApp(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        central_widget = QtGui.QWidget()
        layout = QtGui.QVBoxLayout()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)
    
        label = QtGui.QLabel('This is a label, The widgets will be below')
        label.setMaximumHeight(15)
        ## The DockArea as its name says, is the are where we place the Docks
        dock_area = DockArea(self)
        ## Create the Docks and change some esthetic of them
        self.dock1 = Dock('Widget 1', size=(300, 500))
        self.dock2 = Dock('Widget 2', size=(400, 500))
        self.dock1.hideTitleBar()
        self.dock2.hideTitleBar()
        self.dock1.nStyle = """
        Dock > QWidget {
            border: 0px solid #000;
            border-radius: 0px;
        }"""
        self.dock2.nStyle = """
        Dock > QWidget {
            border: 0px solid #000;
            border-radius: 0px;
        }"""
        self.button = QtGui.QPushButton('Exit')
        self.widget_one = WidgetOne()
        self.widget_two = WidgetTwo()
        ## Place the Docks inside the DockArea
        dock_area.addDock(self.dock1)
        dock_area.addDock(self.dock2, 'right', self.dock1)
        ## The statment above means that dock2 will be placed at the right of dock 1
        layout.addWidget(label)
        layout.addWidget(dock_area)
        layout.addWidget(self.button)
        ## Add the Widgets inside each dock
        self.dock1.addWidget(self.widget_one)
        self.dock2.addWidget(self.widget_two)
        ## This is for set the initial size and posotion of the main window
        self.setGeometry(100, 100, 600, 400)
        ## Connect the actions to functions, there is a default function called close()
        self.widget_one.TitleClicked.connect(self.dob_click)
        self.button.clicked.connect(self.close)
        
    def dob_click(self, feed):
        self.widget_two.text_box.clear()
        ## May look messy but wat i am doing is somethin like this:
        ## 'Title : ' + feed[0]  + '\n\n' + 'Summary : ' + feed[1]
        self.widget_two.text_box.setText(
            'Title : ' + feed[0]\
            + '\n\n' +\
            'Summary : ' + feed[1]
        )
        
class WidgetOne(QtGui.QWidget):
    ## This signal is created to pass a "list" when it (the signal) is emited
    TitleClicked = QtCore.pyqtSignal([list])
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.layout = QtGui.QVBoxLayout()
        self.setLayout(self.layout)
        self.titleList = QtGui.QListWidget()
        self.label = QtGui.QLabel('Here is my list:')
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.titleList)
        
        self.titleList.addItem(QtGui.QListWidgetItem('Title 1'))
        self.titleList.addItem(QtGui.QListWidgetItem('Title 2'))
        self.titleList.itemDoubleClicked.connect(self.onClicked)

    def onClicked(self, item):
        ## Just test values
        title = item.text()
        summary = "Here you will put the summary of {}. ".format(title)*50
        ## Pass the values as a list in the signal. You can pass as much values
        ## as you want, remember that all of them have to be inside one list
        self.TitleClicked.emit([title, summary]) 

class WidgetTwo(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.layout = QtGui.QVBoxLayout()
        self.setLayout(self.layout)
        self.label2 = QtGui.QLabel('Here we show results?:')
        self.text_box = QtGui.QTextBrowser()
        
        self.layout.addWidget(self.label2)
        self.layout.addWidget(self.text_box)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

Again, there are comments inside the code to help you understand what I did. Here is how it looks:

If you pass the mouse between the two widgets you will see the mouse icon will change, with that you can readjust on the run the size of both widgets.

Final Words

This is another approach, more "interactive" and more esthetic than my previous answer. As you said, using a QSplitter works too.




回答2:


Problems

The way you are building your GUI is, in my opinion, messy and it may lead to errors. I suggest the use of Layouts for a more organized GUI.

The other problem is that each widget is an independent class so if you want to connect an action in one widget to do something in the other widget through the Main Window, you must use Signals.

Edit : Another suggestion, use other name for the close function instead of exit and try using self.close() instead of QCoreApplication.instance().quit()

Solution

Trying to emulate what you want to do I made this GUI:

import sys
from PyQt5 import QtGui, QtCore

class MyWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        ## Generate the structure parts of the MainWindow
        self.central_widget = QtGui.QWidget() # A QWidget to work as Central Widget
        self.layout1 = QtGui.QVBoxLayout() # Vertical Layout
        self.layout2 = QtGui.QHBoxLayout() # Horizontal Layout
        self.widget_one = WidgetOne()
        self.widget_two = WidgetTwo()
        self.exitBtn = QtGui.QPushButton('Exit')
        ## Build the structure
         # Insert a QWidget as a central widget for the MainWindow    
        self.setCentralWidget(self.central_widget)  
         # Add a principal layout for the widgets/layouts you want to add
        self.central_widget.setLayout(self.layout1)
         # Add widgets/layuts, as many as you want, remember they are in a Vertical
         # layout: they will be added one below of the other
        self.layout1.addLayout(self.layout2)
        self.layout1.addWidget(self.exitBtn)
         # Here we add the widgets to the horizontal layout: one next to the other
        self.layout2.addWidget(self.widget_one)
        self.layout2.addWidget(self.widget_two)
        ## Connect the signal
        self.widget_one.TitleClicked.connect(self.dob_click)

    def dob_click(self, feed):
        ## Change the properties of the elements in the second widget
        self.widget_two.title.setText('Title : '+feed[0])
        self.widget_two.summary.setText('Summary : '+feed[1])

## Build your widgets same as the Main Window, with the excepton that here you don't
## need a central widget, because it is already a widget.
class WidgetOne(QtGui.QWidget):
    TitleClicked = QtCore.pyqtSignal([list]) # Signal Created
    def __init__(self):
        QtGui.QWidget.__init__(self)
        ## 
        self.layout = QtGui.QVBoxLayout()  # Vertical Layout
        self.setLayout(self.layout)
        self.titleList = QtGui.QListWidget()
        self.label = QtGui.QLabel('Here is my list:')
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.titleList)
        
        self.titleList.addItem(QtGui.QListWidgetItem('Title 1'))
        self.titleList.addItem(QtGui.QListWidgetItem('Title 2'))
        self.titleList.itemDoubleClicked.connect(self.onClicked)

    def onClicked(self, item):
        ## Just test parameters and signal emited
        self.TitleClicked.emit([item.text(), item.text()+item.text()]) 

class WidgetTwo(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.layout = QtGui.QVBoxLayout()
        self.setLayout(self.layout)
        self.title = QtGui.QLabel('Title : ---')
        self.summary = QtGui.QLabel('Summary : ---')
        self.link = QtGui.QLabel('Link : ---')
        self.layout.addWidget(self.title)
        self.layout.addWidget(self.summary)
        self.layout.addWidget(self.link)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

Inside the code, there are comments to help you understand why I did to build an organized GUI. There is also an example of a Signal being used to connect the action of itemDoubleClicked from the first widget to the second one. Here is how the MainWindow looks:

It is not very clear how the layouts work just from seeing the result, so I did a little paint over to a better understanding:

The blue box is the vertical layout (QVBoxLayout) and the red one is the horizontal layout (QHBoxLayout). Inside the blue layout, are located the red layout (above) and the exit button (below); and inside the red layout, are located the widget_1 (left) and the widget_2 (right).

Other Solution

An "easier" solution will be building the widgets inside the MainWindow instead of creating separate classes. With this you will avoid the use of signals, but the code will become a little more confusing because all the code will be cramped in one class.



来源:https://stackoverflow.com/questions/65789123/how-to-have-two-widgets-in-one-main-window

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!