Binding a signal in a custom widget class - python

I'm trying to add a custom Gtk.ListBoxRow to a Gtk.ListBox. This custom row is defined in the CustomRow class in the following way:
class CustomRow(Gtk.ListBoxRow):
def __init__(self, label):
super().__init__()
button = Gtk.Button(label='button')
label = Gtk.Label(label=label)
box = Gtk.Box()
box.add(button)
box.add(label)
self.add(box)
class Main:
def __init__(self):
super().__init__()
...
self.listbox = Gtk.ListBox()
...
self.listbox.add(CustomRow('label'))
def button_clicked(self):
print('button clicked')
if __name__ == '__main__':
main = Main()
main.window.show_all()
Gtk.main()
What I now want to do is have is have the button be bound to button_clicked function found in Main - this function will remove the row from the listbox. Issue is that I have no idea how to do this from another class.

Related

How to set an item to being edited from a button click in PyQt5

I am trying to create a "Rename" button in a GUI I've been working on. When this button is clicked, I want the selected item to go into "Edit mode", where you can type a new name.
I have gotten to the point where everything works, but I don't know how to set the flags properly in order to go into edit mode from the index. Below is a working standalone example.
In this example, if I have selected the item I want to edit, and press F2, then it goes into edit mode. I want to achieve the same thing when I click the "Rename" button.
class MainWindow(QMainWindow):
def __init__(self, app):
super().__init__()
self.main_widget = QWidget()
self.tree = DataBrowserTree()
self.setCentralWidget(self.tree)
self.resize(QSize(1340, 1080))
self.show()
class DataBrowserTree(QTreeView):
def __init__(self):
super(DataBrowserTree, self).__init__()
datafiles_path = r'C:\Repositories\QDscript\DotGUI\TestProjects\Proj_3\datafiles'
self.model = QFileSystemModel()
self.model.setRootPath(datafiles_path)
self.model.setReadOnly(False)
index = self.model.index(datafiles_path)
self.setObjectName('dataBrowserTree')
self.setModel(self.model)
self.setRootIndex(index)
self.setAnimated(False)
self.setIndentation(20)
self.setSortingEnabled(True)
self.sortByColumn(0, Qt.AscendingOrder)
self.setHeaderHidden(True)
self.setSortingEnabled(False)
self.setColumnHidden(1, True)
self.setColumnHidden(2, True)
self.setColumnHidden(3, True)
self.setWindowTitle("Dir View")
def mouseReleaseEvent(self, QMouseEvent):
if QMouseEvent.button() == 2:
self.show_rightclick_menu(QMouseEvent.globalPos())
def show_rightclick_menu(self, pos):
self.rightclick_menu = QMenu()
self.action_rename = QAction(self)
self.action_rename.setText('Rename')
self.action_rename.triggered.connect(lambda: self.clicked('rename'))
self.rightclick_menu.addAction(self.action_rename)
self.rightclick_menu.move(pos.x(), pos.y())
self.rightclick_menu.show()
def clicked(self, click_str):
if click_str == 'rename':
index = self.selectedIndexes()[0] # This gets the correct QModelIndex
print(index) # <PyQt5.QtCore.QModelIndex object at 0x1823D930>
def edit(self, index, trigger, event):
# When F2 is pressed I get:
# index --> QModelIndex object
# trigger --> 8 (the key code)
# event --> QKeyEvent object
return QTreeView.edit(self, index, trigger, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
mw = MainWindow(app)
sys.exit(app.exec_())
You don't have to override the edit() method but use it. On the other hand, instead of overriding the mouseReleaseEvent method that overrides the default behavior, the customContextMenuRequested signal should be used when setting the contextMenuPolicy to Qt::CustomContextMenu as shown below:
class DataBrowserTree(QTreeView):
def __init__(self):
super(DataBrowserTree, self).__init__()
datafiles_path = r'C:\Repositories\QDscript\DotGUI\TestProjects\Proj_3\datafiles'
self.model = QFileSystemModel()
self.model.setRootPath(datafiles_path)
self.model.setReadOnly(False)
index = self.model.index(datafiles_path)
self.setObjectName("dataBrowserTree")
self.setModel(self.model)
self.setRootIndex(index)
self.setAnimated(False)
self.setIndentation(20)
self.setSortingEnabled(True)
self.sortByColumn(0, Qt.AscendingOrder)
self.setHeaderHidden(True)
self.setSortingEnabled(False)
for i in (1, 2, 3):
self.setColumnHidden(i, True)
self.setWindowTitle("Dir View")
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.show_rightclick_menu)
def show_rightclick_menu(self, pos):
ix = self.indexAt(pos)
rightclick_menu = QMenu()
action_rename = QAction(self)
action_rename.setText("Rename")
rightclick_menu.addAction(action_rename)
action = rightclick_menu.exec_(self.viewport().mapToGlobal(pos))
if action == action_rename:
self.edit(ix)

How to insert a Qwidget inside Qwidget

I am lost with all the parenting/initialising issues and have no idea why this does not work.
So I create a Label, then I create another Label with some painting in it, them I make a widget that contains the two, then I would like to put this new widget inside the main window... but nothing appears
import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Labhtml(QLabel):
def __init__(self):
super().__init__()
label = QLabel('html')
class Bar(QLabel):
def __init__(self):
super().__init__()
self.resize(100, 5)
def paintEvent(self, e):
qp = QPainter(self)
qp.setBrush(QColor(200, 0, 0))
qp.drawRect(0,0,200,3)
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
widget = QWidget()
html = Labhtml()
bar = Bar()
self.layout = QVBoxLayout(widget)
self.layout.addWidget(html)
self.layout.addWidget(bar)
class Example(QScrollArea):
def __init__(self):
super().__init__()
widget = QWidget()
layout = QVBoxLayout(widget)
layout.addWidget(Wid(widget))
self.setWidget(widget)
self.setWidgetResizable(True)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
First for the class Labhtml, when you inherit from QLabel, you can use the methods and the attributes of the base class, or use the instantiation mechanism to pass some parameters :
class Labhtml(QLabel):
def __init__(self):
super().__init__()
self.setText('html')
Then you don't need to create another widget inside the Wid class, but you have to refer to self instead :
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
html = Labhtml()
bar = Bar()
self.layout = QVBoxLayout(self)
self.layout.addWidget(html)
self.layout.addWidget(bar)
About the instantiation mechanism you could also write the classes by declaring a new text argument (the same used for the Qlabel), and pass it when you create your instance :
class Labhtml(QLabel):
def __init__(self, text):
super().__init__(text)
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
html = Labhtml('html')

QAction not triggered for added QMenu

The issue that I'm facing is when I want to split the functionality of the menubar into multiple files (classes), each of them specific for handling options (File/Help/Edit and so on).
In the Main UI class I have:
class MyFrame(QMainWindow):
def __init__(self):
super().__init__()
self.menu_bar = self.menuBar()
# Create menu
self.add_menu()
def add_menu(self):
help_menu = MenuHelp(self)
def getMenuBar(self):
return self.menu_bar
In the MenuHelp (class):
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__()
self.menu_variable = parrent_widget.getMenuBar().addMenu('Help')
about_action = self.menu_variable.addAction('About')
about_action.setStatusTip('About')
about_action.triggered.connect(self.handle_trigger)
def handle_trigger(self):
print('Im here')
The menubar is correctly shown, but handle_trigger method is never called, any ideas on what am I doing wrong?
You must pass a parent to your QMenu. You must change:
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__()
to:
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__(parrent_widget)

Python public methods inside classes?

I cutted out code for better understanding my issue. The question is if there is some way how to create something like public method and how or whether I'm totally misunderstood OOP concept.
I have a major class PlayerWindow which only plays video. Then there is class ControlsWindow serving only for my developing, testing and maintenance purposes (launched when fullscreen off). Therefore, I want to be it particularly. Can not figure out, how to call method play() from the ControlsWindow class as well like from inside because when I initialize ControlsWindow with instance of PlayerWindow then I get infinite loop.
class ControlsWindow(QtGui.QWidget):
def __init__(self):
super(ControlsWindow, self).__init__()
self.playPauseButton = QtGui.QPushButton('Play', self)
self.show()
class PlayerWindow(QtGui.QWidget):
def __init__(self):
super(PlayerWindow, self).__init__()
# ...
self.mediaPlayer = self.playerInstance.media_player_new()
# ...
self.initUI()
self.play()
def initUI(self):
# ...
self.show()
self.controls_window = ControlsWindow()
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Return:
self.toggleControlsWindow()
def toggleControlsWindow(self):
if self.isFullScreen():
self.showNormal()
self.controls_window = ControlsWindow()
else:
self.controls_window.close()
self.showFullScreen()
def play(self):
self.mediaPlayer.play()
def main():
app = QtGui.QApplication(sys.argv)
player_window = PlayerWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
You can pass an instance of PlayerWindow to the class constructor of ControlsWindow:
class ControlsWindow(QtGui.QWidget):
def __init__(self, parent): # Notice the new parent argument
super(ControlsWindow, self).__init__()
self.parent = parent
self.playPauseButton = QtGui.QPushButton('Play', self)
self.show()
# Now you can call the parent's (PlayerWindow) play() function:
self.parent.play()
class PlayerWindow(QtGui.QWidget):
def __init__(self):
super(PlayerWindow, self).__init__()
# ...
self.mediaPlayer = self.playerInstance.media_player_new()
# ...
self.initUI()
self.play()
def initUI(self):
# ...
self.show()
self.controls_window = ControlsWindow(self) # Pass a reference of PlayerWindow
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Return:
self.toggleControlsWindow()
def toggleControlsWindow(self):
if self.isFullScreen():
self.showNormal()
self.controls_window = ControlsWindow(self) # Pass a reference of PlayerWindow
else:
self.controls_window.close()
self.showFullScreen()
def play(self):
self.mediaPlayer.play()
def main():
app = QtGui.QApplication(sys.argv)
player_window = PlayerWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Hope this helps!
Assuming that I understand you correctly and your code can be simplified to something like:
# QtGui.QWidget
class B:
pass
# ControlsWindow
class Test(B):
def __init__(self):
# You want to call Actual.play here?
pass
# PlayerWindow
class Actual(B):
def __init__(self):
# Your self.mediaPlayer = self.playerInstance.media_player_new()
self.some_variable = 42
def play(self):
print(self.some_variable)
If you'd like to call Actual.play method from inside Test class you can either:
make Actual.play static and self.mediaPlayer a class variable
class Test(B):
def __init__(self):
# Here you go!
Actual.play()
class Actual(B):
# mediaPlayer = self.playerInstance.media_player_new()
some_variable = 42
def __init__(self):
pass
#staticmethod
def play():
print(Actual.some_variable)
or pass a reference to PlayerWindow object to your ControlsWindow class instance
class B:
pass
class Test(B):
def __init__(self, actual: Actual):
# Here you go!
actual.play()
class Actual(B):
def __init__(self):
self.some_variable = 42
def play(self):
print(self.some_variable)

How to override isclick() sigal in Pyqt?

I have a class BooleanButton contains extra boolean flag to toggle every click. After each click, I want it to emit a signal and the slot will receive the boolean flag. I just write the following, but of course, it won't work.
class BooleanButton(QPushButton):
def __init__(self, name):
QPushButton.__init__(self, name)
self.bool = False
def clicked(self, bool):
self.bool = not self.bool
self.emit(self.bool)
After creating the object, it connects to a slot. When I click this button, a swapping true-false signal will send to the slot.
bool_btn.isclicked[bool].connect(widget.func)
Thanks.
First, don't call a method clicked, that will hide the buttons clicked signal.
If you want to define a new signal, you need to do so using QtCore.pyqtSignal, then you can connect the clicked singal to a slot that will in turn emit your custom signal. Example:
class BooleanButton(QPushButton):
isclicked = pyqtSignal(bool)
def __init__(self, name):
QPushButton.__init__(self, name)
self.bool = False
self.clicked.connect(self.on_clicked)
def on_clicked(self, bool):
self.bool = not self.bool
self.isclicked.emit(self.bool)
As three_pineapples said, QPushButton comes with this feature built-in. Here's a simple example illustrating this behaviour.
from PyQt4 import QtGui, QtCore
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.button = QtGui.QPushButton("Click me", self)
self.button.setCheckable(True)
self.lineEdit = QtGui.QLineEdit(self)
self.button.clicked.connect(self.onClicked)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.lineEdit)
def onClicked(self, checked):
if checked:
self.lineEdit.setText("Button checked")
else:
self.lineEdit.setText("Button unchecked")
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
So your BooleanButton is actually just a QPushButton.

Categories

Resources