PySide:改变 window 的 resize/growth 方向

PySide: Changing resize/growth direction of a window

我在 window 的边缘创建了弹出窗口,我希望它在用户输入文本字段时扩展弹出窗口。这目前有效,但 window 正在向右扩展。相反,我希望弹出窗口向左扩展(并保持右边缘固定到位)。

我最接近的例子如下所示。在其中,我通过每个文本输入获取弹出窗口的大小,然后根据其新大小移动弹出窗口。我觉得这应该有效,但事实并非如此。

在输入第一个文本时,弹出窗口跳到我屏幕的左边缘(仅限 x 转换)。在第二个文本输入上,弹出窗口跳回其原始位置。在第三个文本输入时,弹出窗口跳回到屏幕的左边缘。在第四个输入...你明白了。我还想提一下,window 的整体增长看起来像是从弹出窗口的中心而不是右边缘增长。

我注意到单击按钮后它会一直突出显示,直到我的鼠标经过它。这会导致问题吗?

任何想法或更好的方法来实现这种效果都会很棒,谢谢!

from PySide import QtCore, QtGui
import maya.OpenMayaUI as mui
from shiboken import wrapInstance 

def get_parent():
    ptr = mui.MQtUtil.mainWindow()
    return wrapInstance( long( ptr ), QtGui.QWidget )

############################################
class Tool_Window(QtGui.QDialog):
    def __init__(self, parent = get_parent() ):
        super(Tool_Window, self).__init__(parent)

        # Commands
        self.move_UI()
        self.create_gui()
        self.create_layout()
        self.create_connections()

    #-------------------------------------------
    def create_gui(self):
        self.button1 = QtGui.QPushButton()
        self.button1.setMaximumWidth(50)
        self.button2 = QtGui.QPushButton()
        self.button2.setMaximumWidth(50)
        self.button3 = QtGui.QPushButton()
        self.button3.setMaximumWidth(50)

    #-------------------------------------------
    def create_layout(self):
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)
        layout.addWidget(self.button3)
        blank_layout = QtGui.QVBoxLayout()
        main_layout = QtGui.QHBoxLayout( self )
        main_layout.addLayout(blank_layout)
        main_layout.addLayout(layout)
        layout.addStretch()
        self.setLayout(layout)

    #-------------------------------------------
    def move_UI( self ):
        ''' Moves the UI to the cursor's position '''
        pos = QtGui.QCursor.pos()
        self.move(pos.x()+20, pos.y()+15)

    #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    def create_connections(self):
        # Left click
        self.button1.clicked.connect( self.on_left_click1 )
        self.button2.clicked.connect( self.on_left_click2 )
        self.button3.clicked.connect( self.on_left_click3 )

        # Right click delete
        delete = QtGui.QAction(self)
        delete.setText("remove")
        delete.triggered.connect(self.remove_button)
        self.addAction(delete)

    #-----#-----#-----#-----#-----#-----#-----#-----#-----#
    def remove_button(self):
        self.deleteLater()

    def on_left_click1(self):
        self.popup = Popup_Window( self, self.button1 )                    # Passing button in so I can get it's position
        self.popup.show()

    def on_left_click2(self):
        self.popup = Popup_Window( self, self.button2 )      
        self.popup.show()

    def on_left_click3(self):
        self.popup = Popup_Window( self, self.button3 )      
        self.popup.show()

############################################
class Popup_Window( QtGui.QDialog ):
    def __init__( self, toolWindow, button ):
        super( Popup_Window, self ).__init__()

        self.__popup_filter = ClosePopupFilter()
        self.installEventFilter(self.__popup_filter) 
        self.setWindowFlags(QtCore.Qt.Popup)
        '''
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
                            QtCore.Qt.WindowStaysOnTopHint |
                            QtCore.Qt.CustomizeWindowHint |
                            QtCore.Qt.Tool)    
        '''
        self.button_pos = button       
        self.toolWindow = toolWindow                                        
        self.setAttribute( QtCore.Qt.WA_DeleteOnClose )
        self.resize(100, 100) 

        # Commands
        self.create_gui()
        self.create_layout()
        self.create_connections()
        self.move_UI()        

    #-------------------------------------------
    def move_UI( self ):                                                        # Method that I use to place the popup window initially  
        self.line_edit.setFocus() 
        # Get button position                                          
        self.btn_global_point = self.button_pos.mapToGlobal(self.button_pos.rect().topLeft())  
        print self.btn_global_point
        # Get window position  
        self.win_global_point = self.toolWindow.mapToGlobal(self.rect().topLeft()) 
        print self.win_global_point
        # Get popup Size
        self.popup_size = self.mapToGlobal(self.rect().topRight())
        print self.popup_size
        # Move the window
        self.move((self.win_global_point.x()-self.popup_size.x()), self.btn_global_point.y())

    #-------------------------------------------
    def create_gui( self ):
        ''' Visible GUI stuff '''
        self.my_label = QtGui.QLabel("default text")
        self.line_edit = QtGui.QLineEdit()
        self.line_edit.setMaxLength( 30 )
        self.push_btn = QtGui.QPushButton( "Hey" )
        self.push_btn.setMaximumWidth( 30 )

    #-------------------------------------------
    def create_layout( self ):
        self.button_layout = QtGui.QVBoxLayout()
        self.button_layout.addWidget( self.my_label )
        self.button_layout.addWidget( self.line_edit )
        self.button_layout.addWidget( self.push_btn )
        #self.button_layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.button_layout)

    #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    def create_connections( self ):
        self.line_edit.textChanged.connect( self.on_text_changed )

    #-----#-----#-----#-----#-----#-----#-----#-----#-----#
    def on_text_changed( self ):
        typed_name = self.line_edit.text()
        self.my_label.setText(typed_name) 
        self.move_UI()                                                          # I reuse the move method to move the ui on text edit

############################################
class  ClosePopupFilter(QtCore.QObject):
    ''' Close popup window '''
    def  eventFilter(self, target, event):
        if event.type() == QtCore.QEvent.WindowDeactivate:
            target.close()
        return  False



if __name__ == '__main__':
    # Things to fix PySide Maya bug
    try:
        test_ui.close()
        test_ui.deleteLater()
    except:
        pass

    test_ui = Tool_Window()
    test_ui.show()

    try:
        test_ui.show()
    except:
        test_ui.close()
        test_ui.deleteLater()

我对您在 OP 中提供的代码进行了一些修改,并尝试整理一个示例来解决您的特定问题。

我对代码所做的一些修改:

  • 触发Popup_WindowresizeEvent内的move_UI
  • 更正了 Popup_Window 中的方法 move_UI,这样弹出窗口就不会从一个位置闪烁到另一个位置;
  • eventFilter 直接移动到 Popup_Window 内;
  • 合并了 Tool_Window 中处理 button.clicked.connect 事件的函数。

解决方案不完善,弹出window在Ubuntu中展开移动时还有一点'flicker'。然而,这在 Windows7 中不是很明显。如果我想到其他解决方案,我会进行更新。

from PySide import QtCore, QtGui
import sys

class Tool_Window(QtGui.QDialog): #=============================================
    def __init__(self, parent=None): 
        super(Tool_Window, self).__init__(parent)

        self.create_gui()
        self.create_layout()
        self.create_connections()

    def create_gui(self): #=====================================================

        self.button1 = QtGui.QPushButton('Button 1')
        self.button2 = QtGui.QPushButton('Button 2')
        self.button3 = QtGui.QPushButton('Button 3')

    def create_layout(self): #==================================================

        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)
        layout.addWidget(self.button3)
        self.setLayout(layout)

    def create_connections(self): #=============================================

        self.button1.clicked.connect(self.on_lef_click )
        self.button2.clicked.connect(self.on_lef_click )
        self.button3.clicked.connect(self.on_lef_click )

    def on_lef_click(self): #===================================================

        button = self.sender()

        self.popup = Popup_Window(self, button)
        self.popup.show()


class Popup_Window( QtGui.QWidget ): #==========================================
    def __init__( self, parent, button):
        super(Popup_Window, self ).__init__(parent)

        self.setWindowFlags(QtCore.Qt.Popup)

        self.button = button       
        self.parent = parent

        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        #---- set layout key dimension ----

        self.min_width = 100
        self.frame_thick = 10
        self.setFixedWidth(self.min_width)

        #---- init GUI ----

        self.installEventFilter(self)

        self.create_gui()
        self.create_layout()
        self.create_connections()
        self.move_UI()

        self.line_edit.setFocus()


    def create_gui( self ): #===================================================

        self.my_label = QtGui.QLabel("default text")
        self.my_label.setAlignment(QtCore.Qt.AlignRight)
        self.line_edit = QtGui.QLineEdit()
        self.line_edit.setAlignment(QtCore.Qt.AlignRight)
        self.line_edit.setMaxLength( 50 )


    def create_layout( self ): #================================================

        button_layout = QtGui.QGridLayout()

        button_layout.addWidget(self.my_label, 0, 0)
        button_layout.addWidget(self.line_edit, 1, 0)

        button_layout.setContentsMargins(self.frame_thick, self.frame_thick,
                                         self.frame_thick, self.frame_thick)

        self.setLayout(button_layout)


    def create_connections(self): #=============================================
        self.line_edit.textChanged.connect(self.line_edit_text_changed)


    def line_edit_text_changed(self, text):  #==================================

        #---- set the text in label ----

        self.my_label.setText(text)

        #---- determine new width of popup ----

        fm = self.line_edit.fontMetrics()
        txtWidth = fm.boundingRect(text).width() + 10 # Padding to the left.
                                                      # Value is arbitrary.

        newWidth = max(txtWidth + self.frame_thick * 2, self.min_width)

        self.setFixedWidth(newWidth)


    def eventFilter(self, source, event): #=====================================

        if event.type() == QtCore.QEvent.WindowDeactivate:
            self.close()

        return QtGui.QWidget.eventFilter(self, source, event)

    def resizeEvent(self, event): #=============================================

        self.move_UI()        
        QtGui.QWidget.resizeEvent(self, event)


    def move_UI(self): # =======================================================

        y_btn = self.button.mapToGlobal(QtCore.QPoint(0, 0)).y()        
        x_win = self.parent.mapToGlobal(QtCore.QPoint(0, 0)).x()

        w_pop = self.frameGeometry().width()

        x = x_win - w_pop - 12 # 12 is an arbitrary value.
        y = y_btn

        self.move(QtCore.QPoint(x, y))


if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    instance_1 = Tool_Window()
    instance_1.show()
    sys.exit(app.exec_())

这导致:

更新(2015-07-30):弹出窗口缩小到内容的宽度

我已经扩展了示例,现在弹出窗口以最小值 100 开始,当文本宽度超过此值时会扩展。此外,如果删除文本,弹出窗口的宽度将缩小到最小值 100。

这是通过计算 QLineEdit 中文本的宽度来完成的,当它的内容发生变化时,并使用该值为方法 window 中的弹出窗口分配固定宽度 line_edit_text_changed .