- 전체
- Python 일반
- Python 수학
- Python 그래픽
- Python 자료구조
- Python 인공지능
- Python 인터넷
- Python SAGE
- wxPython
- TkInter
- iPython
- wxPython
- pyQT
- Jython
- django
- flask
- blender python scripting
- python for minecraft
- Python 데이터 분석
- Python RPA
- cython
- PyCharm
- pySide
- kivy (python)
pySide [pySide] [번역] PySide6 - 시그널, 슬롯 & 이벤트
2023.12.27 05:48
[pySide] [번역] PySide6 - 시그널, 슬롯 & 이벤트
이 게시글은 공부를 목적으로 https://www.pythonguis.com 의 PySide6 시리즈를 한글로 번역한 것 입니다. 원문의 주소는 포스트 하단에 남겨놓았습니다. 오역, 오타 지적은 언제나 환영 합니다!
지금까지 우리는 간단한 버튼이 있는 window를 만들어봤습니다. 하지만 버튼이 눌러지긴 해도 아무런 동작도하지 않아 쓸모가 없었지요. GUI 앱을 만든다면 뭐라도 해주길 바랄테니까요 ! 우리가 필요한건 버튼이 눌리는 action과 "어떤 일"이 일어나도록 만드는 것 사이를 연결해 주는 일입니다. 이를 위해 Qt에선 signal과 slot 혹은 event를 제공합니다.
Signals & Slot
Signal은 어떤 일이 일어 났을 때 widget으로부터 발생하는 알림 입니다. 어떤 일은 버튼을 누른 것 부터, input box의 텍스트가 바뀌는 일, window의 텍스트가 바뀌는 일 등등 어떤 것이든 될 수 있습니다. 많은 signal들이 유저의 action에 의해 발생하지만, 꼭 그러리라는 법은 없습니다.
signal은 뭔가가 일어났다는 걸 알려줄 수도 있지만, 그 맥락에 대한 추가적인 데이터를 제공하는 것도 가능합니다.
당신만의 custom signal을 만들 수도 있어요. 나중에 함께 살펴보도록 합시다.
slot은 Qt에서 사용하는 signal 수신기의 이름입니다. Python에선 어떤 함수든지 slot이 될 수 있습니다. signal을 연결하는 간단한 동작을 통해서 말이죠. 만약 signal이 데이터를 송신한다면, 이와 결된 함수는 데이터를 수신하게 될 겁니다. 많은 Qt Widget들이 이미 만들어진 slot을 가지고 있습니다.
Qt signal의 기본적인 부분을 살펴보면서 어떻게 widget과 엮어 앱에서 뭔가를 할 수 있는지 알아보도록 합시다.
다음의 코드를 app.py라는 이름으로 저장해봅시다.
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self) -> None:
super().__init__(MainWindow, self).__init__()
self.setWindowTitle("My App")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
QPushButton Signals
우리의 앱은 지금 QPushButton이 한 가운데 있는 QMainWindow를 가지고 있습니다. 버튼을 임의로 만든 파이썬 함수와 연결하는 것 부터 시작해봅시다. 여기서 우리는 the_button_was_clicked 라는 slot을 만들어 QPushButton에서 보내는 클릭 signal을 받아 봅시다.
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self) -> None:
super().__init__()
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
button.setCheckable(True)
button.clicked.connect(self.the_button_was_clicked)
# 화면 중앙에 Widget 설정
self.setCentralWidget(button)
def the_button_was_clicked(self):
print("Clicked!")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
실행해보세요! 버튼을 클릭하면 콘솔 창에 "Clicked!"라는 글자가 보입니다.
Clicked!
Clicked!
Clicked!
Clicked!
데이터 수신하기
좋은 시작이네요! 앞서서 signal은 무슨 일이 일어나는지에 대한 데이터를 제공할 수 있다고 언급했습니다. .clicked 역시 예외는 아닙니다. checked(혹은 토글) 상태를 전달해줄 수 있습니다. 기본적으로 이 기능은 False 상태라서, 우리가 만들었던 slot은 이 데이터를 무시했습니다. 하지만 우리의 버튼을 checkable하게 만들어 결과를 살펴 봅시다.
다음 예제에서, 우리는 checkstate를 알 수 있는 두 번째 slot을 추가해 보겠습니다.
class MainWindow(QMainWindow):
def __init__(self) -> None:
super().__init__()
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
button.setCheckable(True)
button.clicked.connect(self.the_button_was_clicked)
button.clicked.connect(self.the_button_was_toggled)
self.setCentralWidget(button)
def the_button_was_clicked(self):
print("Clicked!")
def the_button_was_toggled(self, checked):
print("Checked?", checked)
실행해보세요! 버튼을 누르면 체크되었다는 표시가 콘솔에 보일 겁니다. 다시 누르면 체크가 해제됩니다.
Clicked!
Checked? True
Clicked!
Checked? False
Clicked!
Checked? True
Clicked!
Checked? False
원한다면 얼마든지 많은 slot을 한 signal에 연결하고 각각의 반응을 확인할 수 있습니다.
데이터 저장하기
widget의 state(상태)를 파이썬 변수에 보관하는 것은 종종 유용할 때가 있습니다. 이렇게 한다면 원본 widget에 접근하지 않고도 상태 값을 사용할 수 있습니다. 이 값은 임의의 변수에 저장할 수도 있고 원한다면 dictionary에 보관할 수 있습니다. 다음 예제에서, 우리는 버튼의 Checked 값을 button_is_checked 변수에 저장해보도록 하겠습니다.
class MainWindow(QMainWindow):
def __init__(self) -> None:
super().__init__()
self.button_is_checked = True
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
button.setCheckable(True)
button.clicked.connect(self.the_button_was_toggled)
button.setChecked(self.button_is_checked)
self.setCentralWidget(button)
def the_button_was_toggled(self, checked):
self.button_is_checked = checked
print(self.button_is_checked)
첫번째로 우리 변수의 기본 값을 True로 셋팅 한다음 widget의 초기 상태로 설정 했습니다. widget의 상태가 변하면, signal을 받아서 변수에 매치시켜 줍니다.
이러한 패턴은 PySide의 어떤 widget이든지 사용할 수 있습니다. 만약 widget이 현재 상태에 대한 signal을 제공하지 않는다면, handler를 통해 직접적으로 값을 받아와야 합니다. 예를들어, 이 예제에서는 checked 상태를 pressed handler를 통해 확인해 보도록 하겠습니다.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.button_is_checked = True
self.setWindowTitle("My App")
self.button = QPushButton("Press Me!")
self.button.setCheckable(True)
self.button.released.connect(self.the_button_was_released)
self.button.setChecked(self.button_is_checked)
self.setCentralWidget(self.button)
def the_button_was_released(self):
self.button_is_checked = self.button.isChecked()
print(self.button_is_checked)
slot에 접근하기 위해, button의 참조를 self로 하고 있습니다.
released signal이 발생될 때, check state 값은 따로 보내지 않습니다. 대신, .isChecked() 함수를 호출해서 handler안에서 check state값을 확인 할 수 있습니다.
인터페이스 바꾸기
여태까지, 우리는 signal을 받아서 콘솔에 출력해봤습니다. 하지만 우리가 버튼을 누를 때, 우리 앱의 인터페이스를 바꾸도록 하려면 어떻게 해야될까요? 우리의 slot 함수를 이용해 버튼을 수정해 봅시다. 텍스트를 바꾸고, 버튼을 비활성화해서 더이상 누를 수 없도록 해볼꺼에요. 또 checkable state 값도 잠시 꺼두도록 하겠습니다.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
self.button = QPushButton("Press Me!")
self.button.clicked.connect(self.the_button_was_clicked)
self.setCentralWidget(self.button)
def the_button_was_clicked(self):
self.button.setText("You already clicked me.")
self.button.setEnabled(False)
# 윈도우 타이틀 바꾸기
self.setWindowTitle("My Oneshot App")
다시한번 말하자면, the_button_was_clicked 함수에서 button에 접근하기 위해 self를 참조하고 있습니다. 버튼의 텍스트를 바꾸기 위해서 .setText()를 호출하며 str(문자열)을 보냈고, 버튼을 비활성화하기 위해서 .setEnabled()를 False 값과 함께 호출했습니다.
실행해보세요! 버튼을 클릭하면 텍스트가 바뀌고 클릭이 불가능한 상태로 바뀌게 될겁니다.
singal을 발생시키는 버튼을 바꾸는 것에는 어떠한 제약사항도 없습니다. slot 함수에서는 당신이 원하는 무엇이든 할 수 있지요. 예를들어, 다음의 구문을 the_button_was_clicked에 추가하면 타이틀이 또다시 변경 될 것입니다.
self.setWindowTitle("A new window title")
대부분의 widget은 자신만의 signal을 가지고 있습니다. QMainWindow도 예외는 아니지요. 바로 다음에 다뤄볼 좀 더 복잡한 예제에서는, QMainWindow의 .windowTitleChanged signal을 custom slot signal에 연결헤 보겠습니다.
다음 예제에서 QMainWindow의 the_window_title_changed slot에 연결 했습니다. 이 slot은 새로운 윈도우 타이틀을 수신 받습니다.
from curses import window
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
import sys
from random import choice
window_titles = [
"My App",
"My App",
"Still my App",
"Still my App",
"What on earth",
"What on earth",
"This is surprising",
"This is surprising",
"Something went wrong",
]
class MainWindow(QMainWindow):
def __init__(self) -> None:
super().__init__()
self.n_times_clicked = 0
self.setWindowTitle("My App")
self.button = QPushButton("Press Me!")
self.button.clicked.connect(self.the_button_was_clicked)
self.windowTitleChanged.connect(self.the_window_title_changed)
self.setCentralWidget(self.button)
def the_button_was_clicked(self):
print("Clicked.")
new_window_title = choice(window_titles)
print("Setting title: %s" % new_window_title)
self.setWindowTitle(new_window_title)
def the_window_title_changed(self, window_title):
print("Window title changed: %s" % window_title)
if window_title == "Something went wrong":
self.button.setDisabled(True)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
먼저 우리는 윈도우의 title들을 설정 했습니다 -- 우리는 여기서 Python의 built-in 함수인 ramdom.choice() 함수를 이용해서 한번에 하나씩 가져옵니다. 우리는 커스텀 slot 함수인 the_window_title_changed를 window의 .windowTitleChanged signal에 연결해 봤습니다.
버튼을 클릭하면 타이틀이 랜덤하게 바뀔 것 입니다. 만약 window의 타이틀이 "Something went wrong"으로 바뀐다면 버튼이 비활성화 됩니다.
실행해보세요! 타이틀이 "Something went wrong"으로 바뀌어서 버튼이 비활성화 될 때까지 버튼을 반복해서 눌러보세요.
이 예제에서 몇가지 아셔야할 것이 있습니다.
첫 번째로, windowTitleChanged signal은 window의 타이틀을 설정 할 때마다 항상 발생하진 않습니다. 이 signal은 이전의 타이틀과 비교했을 때 새로운 타이틀로 바뀌어야만 발생합니다. 만약 같은 타이틀로 여러번 설정 했다면, 이 signal은 처음 한번 만 발생하게 됩니다. 당신의 앱을 사용할 때 예기치 않은 상황 때문에 놀라지 않기 위해서 항상 이중으로 확인하는 습관을 가지도록 합시다.
두 번째로, 어떻게 우리가 signal을 이용해 많은 것들을 연쇄적으로 발생시켰는지 생각해봅시다. 한 가지 동작 -- 버튼 누르기 -- 로 많은 다른 것들을 발생시킬 수 있습니다. 뒤이어 발생하는 것들은 어떤 동작이 이런 영향을 미쳤는지 알 필요가 없습니다. 다만, 단순히 정해진 룰을 따를 뿐 입니다. 이런 종류의 decoupling effect는 GUI 어플리케이션을 만들 떄 알아야하는 핵심 개념 중의 하나 입니다. 앞으로도 종종 이 개념으로 돌아와서 이야기 해보겠습니다.
이 섹션에서 우리는 signal과 slot에 대해 살펴봤습니다. 우리는 간단한 signal과 이것을 이용해 어떻게 데이터 혹은 상태 값을 전달하는지 알아보았습니다. 다음으로 Qt가 제공하는 몇가지 widget과 signal들을 더 확인해 보겠습니다.
widget끼리 직접적으로 연결하기
여태까지 우리는 widget의 signal을 Python 함수를 이용해 연결했습니다. signal이 widget에서 발생하면, 우리의 Python 함수가 호출되고 signal에게 데이터를 받는 식 이었지요. 하지만 signal을 다루기 위해 항상 Python 함수를 이용해야 하는건 아닙니다. widget끼리도 서로 직접적으로 연결해서 사용할 수 있기 때문입니다.
다음 예제에서, 우리는 QLineEdit과 QLabel widget을 만들었습니다. __init__ 함수에서 .textChanged singnal을 QLabel의 .setText 함수에 연결했습니다. 이제 QLineEdit에서 텍스트를 바꿀 때 마다 QLabel에서 .setText를 통해 바뀐 텍스트를 받을 겁니다.
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QVBoxLayout, QWidget
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
self.label = QLabel()
self.input = QLineEdit()
self.input.textChanged.connect(self.label.setText)
layout = QVBoxLayout()
layout.addWidget(self.input)
layout.addWidget(self.label)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
input을 label에 연결했다는 것을 눈치채셨나요? input과 label 둘다 정의되어있어야 합니다. 이 코드는 두가지 widgwet을 layout에 연결하고 window에 세팅 합니다. layout은 나중에 좀 더 디테일하게 다뤄볼 예정입니다, 지금은 잠깐 무시하셔도 됩니다.
실행해보세요! 위쪽의 박스에 텍스트를 입력해보세요, 즉각적으로 바뀌는 label을 보실 수 있습니다.

대부분 Qt widget들은 사용가능한 slot이 있으며, 같은 type을 매개변수로 받는 모든 signal을 연결 할 수 있습니다. widget documentation의 "Public Slot"에는 각 widget이 가지고 있는 모든 slot의 목록이 있습니다. 예를 들어, 이 주소를 한번 방문해보시기 바랍니다. https://doc.qt.io/qt-5/qlabel.html#public-slots[QLabel]
Events
Qt 어플리케이션에서는 유저와 하는 모든 상호작용이 event입니다. Qt에는 다양한 이벤트가 있고, 각각 다른 상호작용을 표현 합니다. Qt는 event 객체를 사용해 어떤 일이 발생 했는지를 알려줍니다. 이 event들은 상호작용이 일어난 widget에 각각 특정한 event handler와 함께 전달 됩니다. 커스텀 event handler를 정의하거나, 상속받은 event handler들을 사용함으로써, widget에서 event에 응답하는 방식을 대체 할 수 있습니다. Event handler들은 다른 함수들과 마찬가지로 정의 할 수 있지만, 이름 만큼은 event가 반응하는 handler에 따라 정해져 있습니다.
widget이 수신하는 많은 이벤트 중에 가장 빈도수가 높은 것은 QMouseEvnet 입니다. QMouseEvnet는 유저가 widget 위에서 마우스를 움직일 때, 혹은 마우스 버튼을 클릭할 때 발생 합니다. 다음 표에서 마우스 event가 다룰 수 있는 event handler들을 볼 수 있습니다.
| Event handler | Event type moved |
|---|---|
| mouseMoveEvent | 마우스가 움직임 |
| mousePressEvent | 마우스 버튼 눌림 |
| mouseReleaseEvent | 마우스 버튼이 눌렸다 풀림 |
| mouseDoubleClickEvent | 더블 클릭 |
예를 들어, widget을 클릭하게 되면, QMouseEvent가 .mousePressEvent handler를 widget에 보내게 됩니다. 이 handler에서는 event 객체를 이용해 어떤 일이 일어 났는지, 정확히 말하면 어떤 상호작용이 event를 발생시켰는지 알아 낼 수 있습니다.
sub-class를 이용하거나, handler 함수를 overriding 함으로써 event를 중간에 가로 챌 수도 있습니다. 당신은 특정 event를 거르거나, 수정, 또는 무시 할 수도 있고 super()를 이용해 부모 클래스의 handler에 전달 할 수도 있습니다. 다음 예제에서처럼 MainWindow 클래스에 매개변수(이 예시에서는 e)를 추가해서 event를 수신 할 수 있습니다.
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QTextEdit
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.label = QLabel("Click in this window")
self.setCentralWidget(self.label)
def mouseMoveEvent(self, e):
self.label.setText("mouseMoveEvent")
def mousePressEvent(self, e):
self.label.setText("mousePressEvent")
def mouseReleaseEvent(self, e):
self.label.setText("mouseReleaseEvent")
def mouseDoubleClickEvent(self, e):
self.label.setText("mouseDoubleClickEvent")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
실행해보세요! 마우스를 클릭(혹은 더블클릭) 하거나 이리저리 움직여서 화면에 나타나는 텍스트의 변화를 살펴보세요.
mouse move evnet는 오직 마우스 버튼을 누르고 있는 동안만 발생한다는 것을 눈치채셨나요? self.setMouseTracking(True)을 호출하면 바꿀 수 있습니다. press(클릭) event와 더블 클릭 event 모두 버튼이 눌렸을 때 발생했지요. release event만이 버튼을 눌렀다 땠을 때 발생 했습니다. 일반적으로 유저로부터 click event를 받기 위해선 마우스 down과 release 모두 눈여겨 봐야 합니다.
당신은 event handler에서 event 객체에 접근했습니다. 이 객체는 event에 대한 정보를 담고 있고 구체적으로 어떤 행동이 이 event를 발생 시켰는지에 따라 어떻게 반응 하는지 결정할 때 활용할 수 있습니다.
mouse event
Qt의 모든 마우스 event는 QmouseEvent 객체의 읽을 수 있는 정보와 함께 활용 됩니다.
| Method | Returns |
|---|---|
| .button() | 이벤트를 발생 시킨 버튼 정보 |
| .buttons() | 모든 버튼의 상태 값 |
| .globalPos() | 절대 위치(QPoint) |
| .globalX() | 절대 X 좌표 값(수평) |
| .globalY() | 절대 Y 좌표 값(수직) |
| .pos() | widget 상대 좌표 값(QPoint,정수형) |
| .posF() | widget 상대 좌표 값(QPoint,부동소수점) |
이러한 메소드를 활용해서 각기 다른 event에 각기 다른 방식으로 사용하거나, 완전히 무시할 수도 있습니다. global 혹은 local(widget에 상대적인) 좌표 값은 눌린 버튼에서 Qt 네임스페이스의 형식으로 사용되는 동시에 QPoint라는 객체로 전달 됩니다.
예를 들어, 다음의 예제는 마우스 버튼이 왼쪽이냐 오른쪽이냐 가운데 버튼이냐에 따라 다르게 반응 하고 있습니다.
def mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
# 왼쪽 버튼 눌림을 이곳에서 다룹니다.
self.label.setText("mousePressEvent LEFT")
elif e.button() == Qt.MiddleButton:
# 가운데 버튼 눌림은 이곳에서 다룹니다.
self.label.setText("mousePressEvent MIDDLE")
elif e.button() == Qt.RightButton:
# 오른쪽 버튼 눌림은 이곳에서 다룹니다.
self.label.setText("mousePressEvent RIGHT")
def mouseReleaseEvent(self, e):
if e.button() == Qt.LeftButton:
self.label.setText("mouseReleaseEvent LEFT")
elif e.button() == Qt.MiddleButton:
self.label.setText("mouseReleaseEvent MIDDLE")
elif e.button() == Qt.RightButton:
self.label.setText("mouseReleaseEvent RIGHT")
def mouseDoubleClickEvent(self, e):
if e.button() == Qt.LeftButton:
self.label.setText("mouseDoubleClickEvent LEFT")
elif e.button() == Qt.MiddleButton:
self.label.setText("mouseDoubleClickEvent MIDDLE")
elif e.button() == Qt.RightButton:
self.label.setText("mouseDoubleClickEvent RIGHT")
Qt 네임스페이스에서 버튼 종류를 알 수 있는 방법은 다음과 같습니다.
| Identifier | value(binary) | Represents |
|---|---|---|
| Qt.NoButton | 0 (000) | 눌린 버튼이 없음, 혹은 버튼과 관련된 이벤트가 아님. |
| Qt.LeftButton | 1 (001) | 왼쪽 버튼이 눌림 |
| Qt.RightButton | 2 (010) | 오른쪽 버튼이 눌림 |
| Qt.MiddleButton | 4 (100) | 가운데 버튼이 눌림 |
왼손잡이용 마우스에서는 버튼이 반대로 인식 될 것 입니다. 예를 들어 오른쪽 버튼을 누르면 Qt.LeftButton이 되는 식이죠. 이건 당신이 어떤 종류의 마우스를 사용 중인지 일일히 코드로 등록할 필요가 없다는 말 입니다.
Contextmenus
context menu는 일반적으로 오른쪽 클릭을 했을 때 나타나는 작은 메뉴창 입니다. Qt에서 이 메뉴를 생성하는 기능을 제공하고 있습니다. widget이 어떤 이벤트에 반응할 지도 포함해서 말이죠. 다음 예제에서 우리는 QMainWindow의 .contextMenuEvent를 가로채볼 것 입니다. 이 event는 context menu 화면이 나타나기 직전에 발생하며 QContextMenuEvent 타입의 값을 전달 합니다.
event를 가로채기 위해 우리는 객체의 함수를 같은 이름으로 오버라이딩 할 것 입니다. 즉, 이 MainWindow 클래스의 contextMenuEvent라는 이름의 함수에서 같은 타입의 이벤트를 수신 받을 것 입니다.
import sys
from PySide6.QtCore import Qt
from PySide6.QtGui import QAction
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QMenu
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
def contextMenuEvent(self, e):
context = QMenu(self)
context.addAction(QAction("test 1", self))
context.addAction(QAction("test 2", self))
context.addAction(QAction("test 3", self))
context.exec_(e.globalPos())
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
위의 코드를 실행하고 window위에서 오른쪽 클릭을 하면 context menu가 나타납니다. .triggered slot을 이용해서 menu action을 세팅하는 법도 있습니다(이 경우 정의된 actions을 재사용할 수도 있겠습니다.)
좌표값을 exec_ 함수에 넘겨 줄때는 반드시 상속된 부모 클래스와 연관될 수 있어야 합니다. 이 경우에는 절대 좌표값을 사용하기 위해서 self를 이용했습니다.
완벽하게 하기 위해서, signal을 이용한 context menu 생성법도 같이 알아보겠습니다.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.show()
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.on_context_menu)
def on_context_menu(self, pos):
context = QMenu(self)
context.addAction(QAction("test 1", self))
context.addAction(QAction("test 2", self))
context.addAction(QAction("test 3", self))
context.exec_(self.mapToGlobal(pos))
어떤 방식으로 할지는 전적으로 당신의 선택에 달려있습니다.
Event hieratchy
PySide6에서는 모든 widget들이 두가지 계층을 가지고 있습니다. Python object 계층, Qt 레이아웃 계층 입니다. event에 무시하거나 응답하는 방식에 따라 UI의 동작이 달라집니다.
Python inheritance forwading
종종 event를 가로채고 뭔가를 하면서 동시 기본적으로 마련되어 있는 event handler도 동작하길 원할 때가 있습니다. 만약 당신의 object가 일반적인 widget을 상속 받았다면, 기본적으로 가장 합리적인 인터페이스를 구현하고 있을 확률이 높습니다. 당신은 super() 함수를 호출해서 부모 클래스의 구현체를 가져올 수 있습니다.
PySide의 .parent() 함수가 아닌 Python의 부모 클래스 임을 유의하시기 바랍니다.
def mousePressEvent(self, event):
print("Mouse pressed!")
super(self, MainWindow).contextMenuEvent(event)
이것은 다른 정상적인 event와 마찬가지로 작동하지만, 다른 이벤트의 간섭 없이 동작을 추가 했습니다.
Layout fowarding
당신이 어플리케이션에 widget을 추가하게되면, layout으로부터 또 다른 부모를 상속 받게 됩니다. widget의 부모는 .parent()를 호출하면 찾을 수 있습니다. 당신은 종종 QMenu나 QDialog와 같이 부모를 수동으로 설정해야하는 경우도 있지만, 이미 자동으로 지정되어 있는 경우도 있습니다. 예를들어, 당신이 widget을 main window에 추가하면, main window는 자동으로 해당 widget의 부모가 됩니다.
유저와 UI간의 상호작용으로 event가 발생하게 되면, 이 event는 UI의 최상위 widget으로 전달 됩니다. 즉, 당신이 window에 버튼을 가지고 있고, 그 버튼을 누른다면, 그 버튼이 가장 먼저 event를 수신 받게 됩니다.
만약 첫번째 widget이 event를 핸들링 할 수 없거나, 핸들링 하지 않는다면 해당 이벤트는 부모 widget으로 전달됩니다. event가 부모에게 전달되는 현상을 bubbling이라고 하는데, 이는 event handler 혹은 main window에 닿을때까지 계속 됩니다.
당신의 event handler에서 .accept()를 호출 함으로써 event를 핸들링 했다고 마크 할 수도 있습니다.
class CustomButton(QPushButton)
def mousePressEvent(self, e):
e.accept()
혹은 .ignore()를 호출해서 핸들링을 하지 않을 수도 있습니다. 이 경우 event는 상위 계층으로 계속 bubbling 될 것 입니다.
class CustomButton(QPushButton)
def event(self, e):
e.ignore
만약 widget이 event에 반응하게 만들고 싶지 않다면 안전하게 무시할 수 있습니다. 비슷하게, event를 accept하고 아무런 응답도 하지 않을수도 있습니다.
원문: https://www.pythonguis.com/tutorials/pyside6-signals-slots-events/#menu
[출처] https://velog.io/@kne666/%EB%B2%88%EC%97%AD-PySide6-%EC%8B%9C%EA%B7%B8%EB%84%90-%EC%8A%AC%EB%A1%AF-%EC%9D%B4%EB%B2%A4%ED%8A%B8
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.

