Setting button signals for widget created via wrapper function - python

I am trying to create a GUI (see attached screenshot) where it has about 6 sets of widgets where each set comprises of QLabel, QLineEdit and QPushButton.
Instead of writing the widgets and the layout again and again, I thought of writing up a wrapper function such that I will only need to write up 6 lines for the creation.
However, while this works efficiently for me in terms of the widgets creation, I realized that I am not able to set the Signal for the button(s) as I do not know the widget's names.
What will be the best way to set the signals if I decided to use this wrapper methodology?
The following is a screenshot of what I am trying to achieve as well as my code:
class MyDevTool(QtGui.QWidget):
def __init__(self, parent=None):
super(MyDevTool, self).__init__(parent)
self._create_ui()
def _create_hbox_layout(self, label):
h_layout = QtGui.QHBoxLayout()
label = QtGui.QLabel(label)
label.setFixedWidth(80)
line_edit = QtGui.QLineEdit()
push_btn = QtGui.QPushButton("<<<")
h_layout.addWidget(label)
h_layout.addWidget(line_edit)
h_layout.addWidget(push_btn)
return h_layout
def _create_ui(self):
main_layout = QtGui.QVBoxLayout()
main_layout.addLayout(
self._create_hbox_layout("Input Directory")
)
main_layout.addLayout(
self._create_hbox_layout("Output Directory")
)
self.setLayout(main_layout)

Related

How to fix PyQgis layout resizing problem when making window bigger?

I'm working on creating python plugin that implements image viewer and use layout to plased elements in correct plases. I want to resize all elements in dialog window of this plugin using resizeEvent of QDialog, but I have one strange problem: when I want to make dialog window smaller - all works good like on image1, but, if I want to make window bigger I have seen a problem like on image2:
Here is my resizeEvent method for my dialog:
def resizeEvent(self, a0: QResizeEvent):
geom = list(self.geometry().getCoords())
x_shift = geom[2] - geom[0] #transform x coord from monitor coords to dialog window coords
y_shift = geom[3] - geom[1] #transform y coord from monitor coords to dialog window coords
self.verticalLayout.setGeometry(QRect(0, 0, x_shift, y_shift))
And here is my code how I connect my ui with python code using uic:
import os
from qgis.PyQt import uic
from qgis.PyQt import QtWidgets
FORM_CLASS, _ = uic.loadUiType(os.path.join(
os.path.dirname(__file__), 'name_of_ui'))
class nameDialog(QtWidgets.QDialog, FORM_CLASS):
def __init__(self, parent=None):
super(status_checkerDialog, self).__init__(parent)
self.setupUi(self)
In this script, I create the resize function. Also, I would like to add that I use to build the skeleton of the module named Plugin Builder. Maybe this information will help you point out exactly where I am making a mistake. I also add the code skeleton that implements all functions of the module:
class status_checker:
def __init__(self, iface):
"Constructor"
def add_action(
self,
icon_path,
text,
callback,
enabled_flag=True,
add_to_menu=True,
add_to_toolbar=True,
status_tip=None,
whats_this=None,
parent=None):
"""Add a toolbar icon to the toolbar"""
def initGui(self):
"""Create the menu entries and toolbar icons inside the QGIS GUI"""
def unload(self):
"""Removes the plugin menu item and icon from QGIS GUI."""
def run(self):
"""Run method that performs all the real work"""
# Create the dialog with elements (after translation) and keep reference
# Only create GUI ONCE in callback, so that it will only load when the plugin is started
if self.first_start == True:
self.first_start = False #self.first_start is a variable that is created in the initialization function and initially set to true
self.dlg = status_checkerDialog()

Python PyQt5 how to show the full QMenuBar with a QWidget

I'm getting this weird result when using QMenuBar I've used this exact code before for the QMenuBar and it worked perfectly. But it doesn't show more than 1 QMenu
This is my code:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
from functools import partial
class MainMenu(QWidget):
def __init__(self, parent = None):
super(MainMenu, self).__init__(parent)
# background = QWidget(self)
lay = QVBoxLayout(self)
lay.setContentsMargins(5, 35, 5, 5)
self.menu()
self.setWindowTitle('Control Panel')
self.setWindowIcon(self.style().standardIcon(getattr(QStyle, 'SP_DialogNoButton')))
self.grid = QGridLayout()
lay.addLayout(self.grid)
self.setLayout(lay)
self.setMinimumSize(400, 320)
def menu(self):
menubar = QMenuBar(self)
viewMenu = menubar.addMenu('View')
viewStatAct = QAction('Dark mode', self, checkable=True)
viewStatAct.setStatusTip('enable/disable Dark mode')
viewMenu.addAction(viewStatAct)
settingsMenu = menubar.addMenu('Configuration')
email = QAction('Set Email', self)
settingsMenu.addAction(email)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainMenu()
main.show()
sys.exit(app.exec_())
Result:
I am aware that I am using QWidget when I should be using QMainWindow But is there a workaround???
(I apologize in advance for the terrible quality of the image, there is no good way to take a picture of a QMenuBar)
The problem is that with a QWidget you are not using the "private" layout that a QMainWindow has, which automatically resizes specific children widgets (including the menubar, the statusbar, the dock widgets, the toolbars and, obviously, the "centralWidget").
Remember that a QMainWindow has its own layout (which can't and shouldn't be changed), because it needs that specific custom layout to lay out the aforementioned widgets. If you want to set a layout for the main window, you'll need to apply it to its centralWidget.
Read carefully how the Main Window Framework behaves; as the documentation reports:
Note: Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.
In order to work around that when using a basic QWidget, you'll have to manually resize the children widgets accordingly. In your case, you only need to resize the menubar, as long as you have a reference to it:
def menu(self):
self.menubar = QMenuBar(self)
# any other function has to be run against the *self.menubar* object
viewMenu = self.menubar.addMenu('View')
# etcetera...
def resizeEvent(self, event):
# calling the base class resizeEvent function is not usually
# required, but it is for certain widgets (especially item views
# or scroll areas), so just call it anyway, just to be sure, as
# it's a good habit to do that for most widget classes
super(MainMenu, self).resizeEvent(event)
# now that we have a direct reference to the menubar widget, we are
# also able to resize it, allowing all actions to be shown (as long
# as they are within the provided size
self.menubar.resize(self.width(), self.menubar.height())
Note: you can also "find" the menubar by means of self.findChild(QtWidgets.QMenuBar) or using the objectName, but using an instance attribute is usually an easier and better solution.
Set minimum width
self.setMinimumSize(320,240)

PyQt: How can i set stylesheet contents on scaled mode?

I had an application that contained a lot of widgets with stylesheet on them, However, I did not add any layout to interface, It neither had central widget included, But the application was running without any problems.
However, whenever i tried to resize the application (scaling it down) the widgets would not scale, of course.
I had an little research (Because i could not find anything else related to my problem) and i found this on Qt Documentation, stylesheet reference:
"The actual image that is drawn is determined using the same algorithm as QIcon (i.e) the image is never scaled up but always scaled down if necessary."
How can i make stylesheet scale down with window? (If stylesheet has background image on)
For example i have button with stylesheet:
btn = QtGui.QPushButton(self)
btn.move(0, 0)
btn.setObjectName('btn)
btn.setStyleSheet("#btn {background-image: url(':/images/somepicture.png'); border: none; }")
How can i make this button scale down with window, Can i achieve this without layouts? If not how can i do it with layouts? (without it limiting too much)
If you add the button as the central widget to a QMainWindow it should automatically adjust it's size to fit the available space. However, to get the button image to scale, you need to set the image as a border-image stylesheet property (a little strange). A working example for PyQt4:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
btn = QtGui.QPushButton(self)
btn.setStyleSheet("border-image: url('somepicture.png');") # Scaled
#btn.setStyleSheet("background-image: url('somepicture.png');") # Not scaled
self.setCentralWidget(btn)
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
Note that you don't need to set an id (objectName) to assign the CSS to a specific widget, you can simply pass in the CSS rule via .setStyleSheet().
You cannot set a layout on QMainWindow as it already has a complex layout system to accommodate docking widgets and toolbars. Therefore, if you want to use a layout to add more than one widget to the window, you need to use a container widget to hold it. The following working example demonstrates this:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
w = QtGui.QWidget() # container widget
l = QtGui.QVBoxLayout() # your layout
w.setLayout(l) # set the layout on your container widget
btn = QtGui.QPushButton(self)
btn.setStyleSheet("border-image: url('somepicture.png');")
label = QtGui.QLabel('Hello!')
l.addWidget(btn) # add your widget to the layout
l.addWidget(label) # add the label to the layout
self.setCentralWidget(w) # add the container widget to the QMainWindow
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
If you want to be able to position widgets absolutely, rather than adding them to a layout (which will control their size/position) you can pass the parent element (relative to which x,y coords are taken) when creating it:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
w = QtGui.QWidget() # container widget
btn = QtGui.QPushButton(w)
btn.move(100,100)
btn.setStyleSheet("border-image: url('somepicture.png');")
self.setCentralWidget(w) # add the container widget to the QMainWindow
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
But positioning a widget absolutely like this loses you the ability to auto-scale it to fit the parent widget. If you just want some padding/spacing around the element in the window, take a look at .setContentsMargins on the QLayouts, e.g. l.setContentsMargins(50,50,50,50) will put a 50px margin around the button.

Good way of implementing more windows in PyQt

How to open a new window after the user clicks a button is described here:
https://stackoverflow.com/a/21414775/1898982
and here:
https://stackoverflow.com/a/13519181/1898982
class Form1(QtGui.QWidget, Ui_Form1):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.button1.clicked.connect(self.handleButton)
self.window2 = None
def handleButton(self):
if self.window2 is None:
self.window2 = Form2(self)
self.window2.show()
class Form2(QtGui.QWidget, Ui_Form2):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
I want to develop a GUI application that consists of several steps. Once the user clicks next, the current window closes and another window opens. Technically I can do this like it is described above: Each window opens a new one. After a few steps this is pretty much nested.
Is there a better way to do this?
I would like to have the control flow in my main. Something like this:
main()
window1 = win2()
window1.show()
wait until button in window1 is clicked, then
window1.close()
window2 = win2()
window2.show()
wait until button in window2 is clicked, then
window1.close()
....
I would recommend to use QWizard or QStackedWidget class to perform this task. You can easily switch between widgets or windows using either of these two classes. Refer to QWizard and QStackedWidget docs.
Qt provides special widget for such cases, which called QWidget. Look at it. It's also available in Qt4.

PyQT4 WheelEvent? how to detect if the wheel have been use?

im trying to find out in PyQT how can i set the Mousewheel event?
i need it so i can attach it to the Qscroll area
the code im using is working fine. but the size is hardcoded. i need it to somehow dynamically adjust depending on how the wheel(on the mouse) is used.. like when i slide the mouse wheel up. the height of my frame extends(like 50pixels per tick) and vise versa.
self.scrollArea = QtGui.QScrollArea()
#set the parent of scrollArea on the frame object of the computers
self.scrollArea.setWidget(self.ui.Main_Body)
self.scrollArea.setWidgetResizable(True)
#add the verticalLayout a object on PYQT Designer (vlayout is the name)
#drag the frame object of the computers inside the verticalLayout
#adjust the size of the verticalLayout inside the size of the frame
#add the scrollArea sa verticalLayout
self.ui.verticalLayout.addWidget(self.scrollArea)
self.ui.Main_Body.setMinimumSize(400, 14000)
the last part is what i want to enhance. i dont want it to be hardcoded into a 14000 value.
thanks to anyone who will help. and i hope that the given sample code can also help others in need.
)
I might be a little confused on your question, but here's an example on how to get access to wheel events that resize your window. If you're using a QScrollArea I don't know why you would want to do this though.
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QHBoxLayout(self)
layout.addWidget(Scroll(self))
class Scroll(QScrollArea):
def __init__(self, parent=None):
super(Scroll, self).__init__(parent)
self.parent = parent
def wheelEvent(self, event):
super(Scroll, self).wheelEvent(event)
print "wheelEvent", event.delta()
newHeight = self.parent.geometry().height() - event.delta()
width = self.parent.geometry().width()
self.parent.resize(width, newHeight)
app = QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
If you look at the documentation for QScrollArea you'll see the line of inherited from the QWidget class which has a function called wheelEvent. You can put that in and overwrite the inherited function.

Categories

Resources