简体   繁体   English

PyQt5 QMainWindow,QDockWidget,适合使用screenize自动调整大小

[英]PyQt5 QMainWindow, QDockWidget, fitting autosize with screensize

I've created a QMainWindow with menubar and 4 dockable widgets . 我用菜单栏和4个dockable widgets创建了一个QMainWindow First dockwidget contents multipletabs , second is Qpainter widget , third is Matlabplot and fourth is pdf report . 第一个dockwidget内容multipletabs ,第二个是Qpainter widget ,第三个是Matlabplot ,第四个是pdf report

When I run the code shows up like this below. 当我运行代码时,如下所示。

在此输入图像描述

I want to be like below. 我希望如下。

在此输入图像描述

I want to divide screen into four widget automatically whenever it runs at any screen, And I want to have tabs to resize to its content. 我想在任何屏幕上运行时自动将屏幕划分为四个小部件,并且我希望有标签来调整其内容的大小。

Or do you have any better idea of having such widget, you are welcome to come with it. 或者你有更好的想法有这样的小部件,欢迎你带它。

Update of code 更新代码

Resize of Qdockwidget brings this post forward. Qdockwidget的大小调整带来了这篇文章。 It seems that Qt Qdockwidget resize has been an issue for long time ago. 很久以前,Qt Qdockwidget调整大小似乎已成为一个问题。 I find it very difficult to program my Qmainwindow with 4 Qdockwidget, which the dock would fit and resize according to its contents, with other words, child widget. 我觉得用Qdockwidget编写我的Qmainwindow非常困难,它可以根据其内容适合并调整大小,换句话说就是子小部件。 and According to Qt documentation, Qdockwidget resizes and respect the size of child Widgets. 根据Qt文档,Qdockwidget调整并尊重子窗口小部件的大小。 to get straight to problem, my mainwindow has 4 qdockwidgets, I would like to have them resizable according to contents. 直接问题,我的主窗口有4个qdockwidgets,我想根据内容调整它们。

What I have tried and used so far. 到目前为止我尝试过和使用过的东西。 I have used following size functions. 我使用了以下尺寸功能。

self.sizeHint, self.minimumSize(), self.maximumSize() and self.setFixedSize(self.sizeHint()). 

I am able to fix the size of contents in first Qdockwidget by using following codes. 我可以使用以下代码修复第一个Qdockwidget中的内容大小。

self.setFixedSize(self.sizeHint())

Above code is written in the child widgets Class widgets But that is not enough in order to work it, despite following codes needed to run and effect. 上面的代码是在子窗口小部件Class widgets编写的但是,尽管需要运行和生成以下代码,但这还不够。

    self.first.setMinimumSize(self.first.sizeHint())
    self.grid.setMinimumSize(self.grid.sizeHint())
    self.third.setMinimumSize(self.third.sizeHint())
    self.adjustSize()
    self.first.setMinimumSize(self.first.minimumSizeHint())
    self.grid.setMinimumSize(self.grid.minimumSizeHint())
    self.third.setMinimumSize(self.third.minimumSizeHint())

Noting that still my dockwindow does not resize according to child widgets. 注意到仍然我的dockwindow不会根据子窗口小部件调整大小。 Dockwidget expand and increase. Dockwidget扩展和增加。 One may ask, Qdockwidgets could arrange and control by resizeDocks() . 有人可能会问,Qdockwidgets可以通过resizeDocks()来安排和控制。 This code line is used and tried, but still does not get the desired behaviour. 使用并尝试了此代码行,但仍未获得所需的行为。

I have been looking around and could find some relevant questions. 我一直在环顾四周,可以找到一些相关的问题。

C++ resize a docked Qt QDockWidget programmatically? C ++以编程方式调整停靠的Qt QDockWidget的大小?

Forcing a QDockWidget to behave like a central widget when it comes to resizing 在调整大小时强制QDockWidget的行为类似于中央小部件

Create a QDockWidget that resizes to it's contents 创建一个调整其内容大小的QDockWidget

Those questions do not solve my problem. 那些问题并没有解决我的问题。

Visualization of my code launch 可视化我的代码启动

1- When code runs and display on screen. 1-代码运行并在屏幕上显示。

在此输入图像描述

2- Desired and wanted display by first run of software. 2-首次运行软件所需和想要的显示。

在此输入图像描述

3- When user tabs between tabwidgets want to resize to its content as image below. 3-当tabwidgets之间的用户选项卡想要调整其内容时,如下图所示。

在此输入图像描述

4- The code is given below. 4-代码如下。

import sys, os
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget, 
QDesktopWidget, QApplication, QAction, QFileDialog,QColorDialog
from PyQt5.QtWidgets import QPushButton, QMessageBox, QDockWidget, 
QTabWidget, QVBoxLayout, QGroupBox, QHBoxLayout, QFrame, QSplitter
from PyQt5.QtWidgets import QTableWidget, QRadioButton, QListWidget, 
QCheckBox, QTextEdit, QDialog, QSizePolicy
from PyQt5.QtCore import QSize, Qt, QFileInfo, QFile
from PyQt5.QtGui import QIcon, QKeySequence, QPainter, QPalette, QPen, 
QBrush, QTextCursor, QFont

import matplotlib.pyplot as plt
#plt.style.use('ggplot')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import seaborn as sns


iconroot = os.path.dirname(__file__)

class mywindow(QMainWindow):
    def __init__(self):
        super(mywindow, self).__init__()

        self.setMinimumSize(QSize(1200,800))
        self.setWindowTitle('My Graphic Window')

        centralWidget = QWidget(self)
        self.setCentralWidget(centralWidget)

        gridLayout = QGridLayout(self)
        centralWidget.setLayout(gridLayout)

        qtRectangle = self.frameGeometry()
        centerPoint = QDesktopWidget().availableGeometry().center()
        qtRectangle.moveCenter(centerPoint)
        self.move(qtRectangle.topLeft())


        imageroot = QFileInfo(__file__).absolutePath()

        # Greate new action
        newaction = QAction(QIcon(imageroot +'/images/new.png'), '&New', self)
        newaction.setShortcut('Ctrl+N')
        newaction.setStatusTip('New document')
        newaction.triggered.connect(self.newCall) 


        # Greate menu bar and add action
        menubar = self.menuBar()
        filemenu = menubar.addMenu('&Test')
        filemenu.addAction(newaction)


        # Get current screen geometry
        self.Screen = QtWidgets.QDesktopWidget().screenGeometry()
        print(self.Screen, self.Screen.height(), self.Screen.width())


#    def createToolbar(self):
        self.filetoolbar = self.addToolBar('File')
        self.filetoolbar.addAction(newaction)


        self.topleftdockwindow()
        self.toprightdockwindow()



    def newCall(self):
        print('New')


    # Greate dockable subwindow. 

    def topleftdockwindow(self):
        topleftwindow = QDockWidget ('Info',self)
        # Stick window to left or right
        topleftwindow.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.addDockWidget(Qt.TopDockWidgetArea, topleftwindow)

        topleftwindow.setWidget(createtabwidget())
        topleftwindow.resize( topleftwindow.minimumSize() )

        bottomleftwindow = QDockWidget("Matplot",self)
        bottomleftwindow.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        self.addDockWidget(Qt.BottomDockWidgetArea, bottomleftwindow)
        bottomleftwindow.setWidget(createplotwidget())
        self.setDockNestingEnabled(True)     
        topleftwindow.resize( topleftwindow.minimumSize() )

        self.splitDockWidget(topleftwindow, bottomleftwindow , Qt.Vertical)
        #self.resizeDocks((topleftwindow, bottomleftwindow), (40,20), 
#Qt.Horizontal)


        # Greate topright dockwindow. 

    def toprightdockwindow(self):
        toprightdock = QDockWidget ('Plot',self)
        toprightdock = QDockWidget ('Plot',self)
        toprightdock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        self.addDockWidget(Qt.TopDockWidgetArea, toprightdock)
        #self.setDockOptions(self.AnimatedDocks | self.AllowNestedDocks)
        toprightdock.setWidget(createpaintwidget())
        toprightdock.setFloating( True )
        bottomrightdock = QDockWidget("Technical report",self)
        bottomrightdock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        self.addDockWidget(Qt.BottomDockWidgetArea, bottomrightdock)  
        bottomrightdock.setWidget(QtWidgets.QListWidget())
        self.splitDockWidget(toprightdock, bottomrightdock, Qt.Vertical)




class createpaintwidget(QWidget):
    def __init__(self):
        super().__init__()      
        self.setBackgroundRole(QPalette.Base)       
        self.setAutoFillBackground(True)
        self.sizeHint()
        self.adjustSize()

    def paintEvent(self, event):
        self.pen = QPen()
        self.brush = QBrush(Qt.gray,Qt.Dense7Pattern)
        painter = QPainter(self)
        painter.setPen(self.pen)
        painter.setBrush(self.brush)
        painter.drawRect(100,100,250,250)
        painter.setBrush(QBrush())
        painter.drawEllipse(400,100,200,200)


class createplotwidget(QWidget):
    def __init__(self):
        super().__init__()

        self.initializewidget()
        self.plot1()
        self.setMaximumSize(self.sizeHint())
        self.adjustSize()    

    def initializewidget(self):
        self.setWindowTitle("Plotting M&N")
        gridlayout = QGridLayout()
        self.setLayout(gridlayout)

        self.figure = plt.figure(figsize=(15,5))
        self.canvas = FigureCanvas(self.figure)
        self.toolbar = NavigationToolbar(self.canvas,self)
        gridlayout.addWidget(self.canvas,1,0,1,2)
        gridlayout.addWidget(self.toolbar,0,0,1,2)


    def plot1(self):
 #        sns.set()
        ax = self.figure.add_subplot(111)
        x = [i for i in range(100)]
        y = [i**2 for i in x]
        ax.plot(x,y, 'b.-')
        ax.set_title('Quadratic Plot')
        self.canvas.draw()



class createtextdocument(QWidget):
    def __init__(self):
        super().__init__()

        self.textedit()

    def textedit(self):        
        self.textedit = QTextEdit()
        self.cursor = self.textedit.textCursor()


class createtabwidget(QDialog):
    def __init__(self):
        super().__init__()

        # Greate tabs in dockable window        
        tab = QTabWidget()

        scroll = QScrollArea()
        ncroll = QScrollArea()
        mcroll = QScrollArea()
        self.first = firsttabgeometry()
        self.grid = Grid()
        self.third = thirdtabloads()

        scroll.setWidget(self.first)
        ncroll.setWidget(self.grid)
        mcroll.setWidget(self.third)
        scroll.setWidgetResizable(True)

        self.first.setMinimumSize(self.first.sizeHint())
        self.grid.setMinimumSize(self.grid.sizeHint())
        self.third.setMinimumSize(self.third.sizeHint())
        self.adjustSize()
        self.first.setMinimumSize(self.first.minimumSizeHint())
        self.grid.setMinimumSize(self.grid.minimumSizeHint())
        self.third.setMinimumSize(self.third.minimumSizeHint())

        # Adding multiple tabslides         
        tab.addTab(self.first,'One')
        tab.addTab(self.grid,'Two')
        tab.addTab(self.third,'Three')
        tab.setFont(QFont("Georgia",10,QFont.Normal))

        vboxlayout = QVBoxLayout()
        vboxlayout.addWidget(tab)
        self.setLayout(vboxlayout)


class firsttabgeometry(QWidget):
    def __init__(self):
        super().__init__()
        self.setFixedSize(self.sizeHint())

        iconroot = QFileInfo(__file__).absolutePath()
        font = QFont("Georgia",10,QFont.Normal)

        # Add widget and buttons to tabs
        sectiontypegroupbox = QGroupBox('&One',self)
        sectiontypegroupbox.setFont(QFont("Georgia",10,QFont.Normal))

        tab1button = QPushButton('')
        tab1button.setIcon(QIcon(iconroot +'/images/circularcolumn'))
        tab1button.setIconSize(QSize(60,60))
        tab1button.clicked.connect(self.One)


        squarebutton = QPushButton('')
        squarebutton.setIcon(QIcon(iconroot +'/images/squarecolumn'))
        squarebutton.setIconSize(QSize(60,60))
        squarebutton.clicked.connect(self.Two)

        wallbutton = QPushButton("")
        wallbutton.setIcon(QIcon(iconroot +'/images/wall'))
        wallbutton.setIconSize(QSize(60,60))
        wallbutton.clicked.connect(self.Three)


        circularlabel = QLabel("    One",self)




    circularlabel.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
        circularlabel.setFont(font)
        sclabel = QLabel("    Two",self)
        sclabel.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
        sclabel.setFont(font)

        walllabel = QLabel("    Three",self)
        walllabel.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
        walllabel.setFont(font)


        bottomgroupbox = QGroupBox("Group 2")
        vboxlayout = QHBoxLayout()
        vboxlayout.addStretch()
        radiobutton2 = QRadioButton("Radio Button")
        radiobutton3 = QRadioButton("Radio Button")
        testbutton2 = QPushButton('Test Button 2')
        vboxlayout.addWidget(radiobutton2)
        vboxlayout.addWidget(radiobutton3)
        vboxlayout.addWidget(testbutton2)


        bottomgroupbox.setLayout(vboxlayout)      

        mainlayout = QGridLayout()

        mainlayout.addWidget(tab1button,0,0)
        mainlayout.addWidget(circularlabel,0,1)
        mainlayout.addWidget(squarebutton,1,0)
        mainlayout.addWidget(sclabel,1,1)
        mainlayout.addWidget(wallbutton,2,0)
        mainlayout.addWidget(walllabel,2,1)
        mainlayout.setContentsMargins(200,50,50,50)
        sectiontypegroupbox.setLayout(mainlayout)

        gridlayout = QGridLayout()
        gridlayout.addWidget(sectiontypegroupbox,1,0)
        gridlayout.setContentsMargins(25,25,25,25)
        self.setLayout(gridlayout)


    def One(self):
        print('One')

    def Two(self):
        print('Two')

    def Three(self):
        print('Three')


class FooWidget(QtWidgets.QWidget):
    def __init__(self, path_icon, text, checked=False, parent=None):
        super(FooWidget, self).__init__(parent)
        lay = QtWidgets.QVBoxLayout(self)

        pixmap = QtGui.QPixmap(os.path.join(iconroot, path_icon))
        pixmap_label = QtWidgets.QLabel()
        pixmap_label.resize(150, 150)
        pixmap_label.setPixmap(pixmap.scaled(pixmap_label.size(), QtCore.Qt.KeepAspectRatio))

        text_label = QtWidgets.QLabel(text)
        checkbox = QtWidgets.QCheckBox(checked=checked)

        lay.addWidget(pixmap_label)
        lay.addWidget(text_label)
        lay.addWidget(checkbox)


class Grid(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Grid, self).__init__(parent)
        self.setFixedSize(self.sizeHint())
        font = QFont("Georgia",8,QFont.Normal)
        lay = QtWidgets.QHBoxLayout(self)

        icons = ["images/fixed-fixed.png", 
                 "images/pinned-pinned.png",
                 "images/fixed-free.png",
                 "images/fixed-pinned.png"]

        texts = ["Ley = 1.0 L\nLec = 1.0 L",
             "Ley = 0.699 L\nLec = 0.699 L",
             "Ley = 2.0 L\nLec = 2.0 L",
             "Ley = 0.5 L\nLec = 0.5 L"]

        for path_icon, text in zip(icons, texts):
            w = FooWidget(os.path.join(iconroot, path_icon), text)
            lay.addWidget(w)



class thirdtabloads(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(thirdtabloads, self).__init__(parent)     
        self.adjustSize()
        table = loadtable()


        add_button = QtWidgets.QPushButton("Add")
        add_button.clicked.connect(table._addrow)

        delete_button = QtWidgets.QPushButton("Delete")
        delete_button.clicked.connect(table._removerow)

        copy_button = QtWidgets.QPushButton("Copy")
        copy_button.clicked.connect(table._copyrow)

        button_layout = QtWidgets.QVBoxLayout()
        button_layout.addWidget(add_button, alignment=QtCore.Qt.AlignBottom)
        button_layout.addWidget(delete_button, alignment=QtCore.Qt.AlignTop)
        button_layout.addWidget(copy_button, alignment=QtCore.Qt.AlignTop )

        tablehbox = QtWidgets.QHBoxLayout()
        tablehbox.setContentsMargins(10,10,10,10)
        tablehbox.addWidget(table)

        grid = QtWidgets.QGridLayout(self)
        grid.addLayout(button_layout, 0, 1)
        grid.addLayout(tablehbox, 0, 0) 


def copy_widget(w):
    if isinstance(w, QtWidgets.QWidget):
        new_w = type(w)()
        if isinstance(w, QtWidgets.QComboBox):
            vals = [w.itemText(ix) for ix in range(w.count())]
            new_w.addItems(vals)
        return new_w


class loadtable(QtWidgets.QTableWidget):
    def __init__(self, parent=None):
        super(loadtable, self).__init__(1, 5, parent)

        self.setFont(QtGui.QFont("Helvetica", 10, QtGui.QFont.Normal, italic=False))   
        headertitle = ("Load Name","N [kN]","My [kNm]","Mz [kNm]","Load Type")
        self.setHorizontalHeaderLabels(headertitle)

        self.verticalHeader().hide()
        self.horizontalHeader().setHighlightSections(False)



   self.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Fixed)
        self.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
        self.setColumnWidth(0, 130)

        combox_lay = QtWidgets.QComboBox(self)
        combox_lay.addItems(["ULS","SLS"])
        self.setCellWidget(0, 4, combox_lay)


        self.cellChanged.connect(self._cellclicked)


    @QtCore.pyqtSlot(int, int)
    def _cellclicked(self, r, c):
        it = self.item(r, c)
        it.setTextAlignment(QtCore.Qt.AlignCenter) 

    @QtCore.pyqtSlot()
    def _addrow(self):
        rowcount = self.rowCount()
        self.insertRow(rowcount)
        combox_add = QtWidgets.QComboBox(self)
        combox_add.addItems(["ULS","SLS"])
        self.setCellWidget(rowcount, 4, combox_add)

    @QtCore.pyqtSlot()
    def _removerow(self):
        if self.rowCount() > 0:
            self.removeRow(self.rowCount()-1)


    @QtCore.pyqtSlot()
    def _copyrow(self):
        r = self.currentRow()
        if 0 <= r < self.rowCount():
            cells = {"items": [], "widgets": []}
            for i in range(self.columnCount()):
                it = self.item(r, i)
                if it:
                    cells["items"].append((i, it.clone()))
                w = self.cellWidget(r, i)
                if w:
                    cells["widgets"].append((i, copy_widget(w)))
            self.copy(cells, r+1)

    def copy(self, cells, r):
        self.insertRow(r)
        for i, it in cells["items"]:
            self.setItem(r, i, it)
        for i, w in cells["widgets"]:
            self.setCellWidget(r, i, w)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    app.setStyle("Fusion")
    mainWin = mywindow()
    mainWin.show()
    mainWin.showMaximized()  
    sys.exit(app.exec_())

I would appreciate much any help on this. 我很感激你的帮助。

If floating windows aren't essential to your tool then you can try dropping QDockWidget and using a series of QSplitter instead. 如果浮动窗口对您的工具不是必不可少的,那么您可以尝试删除QDockWidget并使用一系列QSplitter This way you can have your nice box layout while having tabs to resize horizontally and vertically, and still resizing properly when the tool as a whole resizes. 这样你可以拥有漂亮的盒子布局,同时有标签可以水平和垂直调整大小,并且当整个工具调整大小时仍然可以正确调整大小。

My example is in PySide2 , but you'll probably need to do very minor tweaks to PyQt5 (probably just the import names): 我的例子是在PySide2 ,但你可能需要对PyQt5进行非常小的调整(可能只是导入名称):

from PySide2 import QtCore
from PySide2 import QtGui
from PySide2 import QtWidgets


class SubWindow(QtWidgets.QWidget):

    def __init__(self, label, parent=None):
        super(SubWindow, self).__init__(parent)

        self.label = QtWidgets.QLabel(label, parent=self)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setStyleSheet("QLabel {font-size:40px;}")

        self.main_layout = QtWidgets.QVBoxLayout()
        self.main_layout.addWidget(self.label)
        self.setLayout(self.main_layout)


class MainWindow(QtWidgets.QWidget):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.sub_win_1 = SubWindow("1", parent=self)
        self.sub_win_2 = SubWindow("2", parent=self)
        self.sub_win_3 = SubWindow("3", parent=self)
        self.sub_win_4 = SubWindow("4", parent=self)

        self.sub_splitter_1 = QtWidgets.QSplitter(QtCore.Qt.Horizontal, parent=self)
        self.sub_splitter_1.addWidget(self.sub_win_1)
        self.sub_splitter_1.addWidget(self.sub_win_2)

        self.sub_splitter_2 = QtWidgets.QSplitter(QtCore.Qt.Horizontal, parent=self)
        self.sub_splitter_2.addWidget(self.sub_win_3)
        self.sub_splitter_2.addWidget(self.sub_win_4)

        self.splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical, parent=self)
        self.splitter.addWidget(self.sub_splitter_1)
        self.splitter.addWidget(self.sub_splitter_2)

        self.main_layout = QtWidgets.QVBoxLayout()
        self.main_layout.addWidget(self.splitter)
        self.setLayout(self.main_layout)

        self.setWindowTitle("Layout example")
        self.resize(500, 500)


inst = MainWindow()
inst.show()

This gives you something like this: 这给你这样的东西:

在此输入图像描述

Right now the top/bottom horizontal splitters function separately, but you can easily tie them together with an event. 现在,顶部/底部水平分割器可以单独使用,但您可以轻松地将它们与事件绑定在一起。

Hope that helps! 希望有所帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM