2017-04-22 128 views
1

我正在嘗試使GUI顯示(並最終讓用戶構建)電路。下面是應用程序應該是什麼樣子的粗略草圖。PyQt5 - 正確地動態調整大小和佈局組件

Sketch of application

底部面板(現爲簡單QToolBar)應該是恆定的高度,但跨越應用和側板(IOPanel S IN的下面的代碼)的寬度應具有恆定的寬度和跨越應用程序的高度。

應用(Canvas,這也是目前QWidget與重寫paintEvent方法,但可能最終成爲一個QGraphicsView,或者至少一些滾動一個QGraphicsScene),則應該填充剩餘空間的主要部分。

這是我當前的代碼:

from PyQt5.QtWidgets import * 
from PyQt5.QtCore import Qt, QSize 


class MainWindow(QMainWindow): 
    def __init__(self, *args): 
     super().__init__(*args) 
     self._wire_ys = None 
     self._init_ui() 
     self.update_wire_ys() 

    def update_wire_ys(self): 
     self._wire_ys = [(i + 0.5) * self.panel.height()/4 for i in range(4)] 
     self.input.update_field_positions() 
     self.output.update_field_positions() 

    def wire_ys(self): 
     return self._wire_ys 

    def _init_ui(self): 
     self.panel = QWidget(self) 
     self.canvas = Canvas(self, self.panel) 
     self.input = IOPanel(self, self.panel) 
     self.output = IOPanel(self, self.panel) 

     hbox = QHBoxLayout(self.panel) 
     hbox.addWidget(self.canvas, 1, Qt.AlignCenter) 
     hbox.addWidget(self.input, 0, Qt.AlignLeft) 
     hbox.addWidget(self.output, 0, Qt.AlignRight) 

     self.setCentralWidget(self.panel) 
     self.addToolBar(Qt.BottomToolBarArea, self._create_run_panel()) 
     self.reset_placement() 

    def _create_run_panel(self): 
     # some other code to create the toolbar 
     return QToolBar(self) 

    def reset_placement(self): 
     g = QDesktopWidget().availableGeometry() 
     self.resize(0.4 * g.width(), 0.4 * g.height()) 
     self.move(g.center().x() - self.width()/2, g.center().y() - self.height()/2) 

    def resizeEvent(self, *args, **kwargs): 
     super().resizeEvent(*args, **kwargs) 
     self.update_wire_ys() 


class IOPanel(QWidget): 
    def __init__(self, main_window, *args): 
     super().__init__(*args) 
     self.main = main_window 
     self.io = [Field(self) for _ in range(4)] 

    def update_field_positions(self): 
     wire_ys = self.main.wire_ys() 
     for i in range(len(wire_ys)): 
      field = self.io[i] 
      field.move(self.width() - field.width() - 10, wire_ys[i] - field.height()/2) 

    def sizeHint(self): 
     return QSize(40, self.main.height()) 


class Field(QLabel): 
    def __init__(self, *args): 
     super().__init__(*args) 
     self.setAlignment(Qt.AlignCenter) 
     self.setText(str(0)) 
     self.resize(20, 20) 


# This class is actually defined in another module and imported 
class Canvas(QWidget): 
    def __init__(self, main_window, *args): 
     super().__init__(*args) 
     self.main = main_window 

    def paintEvent(self, e): 
     print("ASFD") 
     qp = QPainter() 
     qp.begin(self) 
     self._draw(qp) 
     qp.end() 

    def _draw(self, qp): 
     # Draw stuff 
     qp.drawLine(0, 0, 1, 1) 


# __main__.py 
def main(): 
    import sys 
    app = QApplication(sys.argv) 
    w = MainWindow() 
    w.show() 
    sys.exit(app.exec_()) 


if __name__ == '__main__': 
    main() 

運行的代碼給我下面的:

Result

在這裏,我有彩色的組件,以更好地看到他們使用這樣的代碼在自己建設:

p = self.palette() 
p.setColor(self.backgroundRole(), Qt.blue) 
self.setPalette(p) 
self.setAutoFillBackground(True) 

綠色是中央面板(MainWindow.panel),藍色是IOPanel s,Field s應該是紅色的,而Canvas應該是白色的。

忽略底部的工具欄,它是我上面沒有包括的一些額外代碼(儘可能保持最小和相關),但它沒有調整任何尺寸,也沒有佈局管理,除了它自己的子代碼QWidget。事實上,包括我上面最小例子中的繪畫代碼給出了與沒有運行按鈕的較薄的底部工具欄類似的結果。我只是在這裏包括工具欄,以顯示其在總體佈局中的預期行爲(因爲工具欄工作正常)。

這個結果有幾個問題。

問題1

Field■不要露面,最初。 但是,一旦我調整了主窗口的大小,它們確實出現(並且適當地放置在它們各自的面板內)。爲什麼是這樣?主窗口resizeEvent所做的唯一的事情是update_wire_ysupdate_field_positions,並且這些也是由主窗口的__init__執行的。

問題2

IOPanel s的不正確對齊。第一個應該在中央面板的左側。更改將他們修復了這個順序,像這樣:

hbox.addWidget(self.input, 0, Qt.AlignLeft) 
hbox.addWidget(self.canvas, 1, Qt.AlignCenter) 
hbox.addWidget(self.output, 0, Qt.AlignRight) 

但是,不應該Qt.AlignX已經做到這一點,無論他們是在添加的順序嗎?如果我以後想要在左側添加另一個面板,我將不得不移除所有組件,添加新面板然後重新添加它們?

問題3

IOPanel s的不正確的尺寸。他們需要跨越中央面板的整個高度並觸摸中央面板的左側/右側邊緣。我不確定這是否是佈局或面板着色的問題。我究竟做錯了什麼?

問題4

Canvas完全不露面,事實上它paintEvent不會被調用(「ASFD」永遠不會被打印到控制檯)。我並未覆蓋其sizeHint,因爲我希望中央面板的佈局能夠自行適當調整Canvas的大小。我希望添加組件時的伸展係數爲1可以實現這一點。

hbox.addWidget(self.canvas, 1, Qt.AlignCenter) 

如何讓畫布實際顯示並填充中央面板上的所有剩餘空間?

回答

1

這是典型的意大利麪代碼,其中很多元素都是糾結的,這通常很難測試,我發現很多問題,例如sizeEvent僅在調用包含窗口小部件的佈局時調用,另一個示例是當您使用處理每個其他對象的功能update_field_positionsupdate_wire_ys

在這個答案,我將提出一個簡單的實現:

  • IOPanel CLAS必須包含一個QVBoxLayout,處理圖像大小的變化。

  • MainWindow類中,我們將使用具有對齊的佈局,但必須按順序添加它們。

    lay.addWidget(self.input, 0, Qt.AlignLeft) 
    lay.addWidget(self.canvas, 0, Qt.AlignCenter) 
    lay.addWidget(self.output, 0, Qt.AlignRight) 
    
  • 要放置的最小寬度爲IOPanel我們使用QSizePolicy()setMinimumSize()

完整代碼:

from PyQt5.QtWidgets import * 
from PyQt5.QtGui import * 
from PyQt5.QtCore import * 

class Field(QLabel): 
    def __init__(self, text="0", parent=None): 
     super(Field, self).__init__(parent=parent) 
     self.setAlignment(Qt.AlignCenter) 
     self.setText(text) 


class IOPanel(QWidget): 
    numbers_of_fields = 4 
    def __init__(self, parent=None): 
     super(IOPanel, self).__init__(parent=None) 
     lay = QVBoxLayout(self) 
     for _ in range(self.numbers_of_fields): 
      w = Field() 
      lay.addWidget(w) 

     self.setMinimumSize(QSize(40, 0)) 
     sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) 
     self.setSizePolicy(sizePolicy) 



class Panel(QWidget): 
    def __init__(self, parent=None): 
     super(Panel, self).__init__(parent=None) 
     lay = QHBoxLayout(self) 
     self.input = IOPanel() 
     self.output = IOPanel() 
     self.canvas = QWidget() 

     lay.addWidget(self.input, 0, Qt.AlignLeft) 
     lay.addWidget(self.canvas, 0, Qt.AlignCenter) 
     lay.addWidget(self.output, 0, Qt.AlignRight) 


class MainWindow(QMainWindow): 
    def __init__(self, parent=None): 
     super(MainWindow, self).__init__(parent=parent) 
     self.initUi() 
     self.reset_placement() 

    def initUi(self): 
     panel = Panel(self) 
     self.setCentralWidget(panel) 
     self.addToolBar(Qt.BottomToolBarArea, QToolBar(self)) 

    def reset_placement(self): 
     g = QDesktopWidget().availableGeometry() 
     self.resize(0.4 * g.width(), 0.4 * g.height()) 
     self.move(g.center().x() - self.width()/2, g.center().y() - self.height()/2) 


def main(): 
    import sys 
    app = QApplication(sys.argv) 
    w = MainWindow() 
    w.show() 
    sys.exit(app.exec_()) 


if __name__ == '__main__': 
    main() 

截圖:

enter image description here

+0

我已經設法正確地重構我的代碼,它現在可以工作(並且希望在添加更多內容時繼續工作)。 –

相關問題