Resize QDialog when RadioButton is Checked PyQt - qt

not so many experience at all with pyqt..
I have designed an UI with Qt Designer with 2 Radiobuttons.
Depending on the RadioButton selected some widgets are visible and other not. What I'm not trying to do is to resize automatically the layout of the dialog depending on how many widgets are visible in the UI.
The question is very similar to this one . Here the extract of the code that might be helpful to understand the problem (I know it is just a piece of the code, but the entire dialog is pretty long):
class MyDialog(QDialog, FORM_CLASS):
..........
# connect the radioButton with a function that reloads the UI
self.radioSingle.toggled.connect(self.refreshWidgets)
.....
# dictionary with all the widgets and the values
self.widgetType = {
self.cmbModelName: ['all'],
self.cmbGridLayer: ['all'],
self.cmbRiverLayer: ['all'],
self.lineNewLayerEdit: ['all'],
self.lineLayerNumber: ['single']
}
# function that refresh the UI with the correct widgets depending on the radiobutton selected
def refreshWidgets(self, idx):
'''
refresh UI widgets depending on the radiobutton chosen
'''
tp = self.radioSingle.isChecked()
for k, v in self.widgetType.items():
if tp:
if 'all' or 'single' in v:
active = True
k.setEnabled(active)
k.setVisible(active)
else:
active = 'all' in v
k.setEnabled(active)
k.setVisible(active)
# attempt to resize the Dialog
self.setSizeConstraint()
Surely the code could be improved (not so skilled with code writing)...

You just gotta set_up your UI to resize when it's needed.
There is a lot of more beautiful way to do this, but here is a simple and fast example:
You just have to define your layout to behave the way you want. If you make use of Minimum and Maximum size it'll resize as you need.
You could also have your own personalized buttons, widgets, .... you would have a cleaner code.
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QRadioButton
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget
class CustomDialog(QDialog):
w_container = None
v_layout_container = None
v_scroll_area = None
v_layout_preview = None
def __init__(self):
"""Init UI."""
super(CustomDialog, self).__init__()
self.init_ui()
def init_ui(self):
"""Init all ui object requirements."""
self.r1 = QRadioButton("smaller")
self.r2 = QRadioButton("bigger")
style = "background: green;"
size = [200, 50]
self.small_size = [200, 250]
self.big_size = [200, 500]
self.resize(200,200)
self.w1 = QWidget()
self.w2 = QWidget()
self.w3 = QWidget()
self.w4 = QWidget()
self.w5 = QWidget()
self.w6 = QWidget()
self.w1.setStyleSheet(style)
self.w2.setStyleSheet(style)
self.w3.setStyleSheet(style)
self.w4.setStyleSheet(style)
self.w5.setStyleSheet(style)
self.w6.setStyleSheet(style)
self.w1.setFixedSize(size[0], size[1])
self.w2.setFixedSize(size[0], size[1])
self.w3.setFixedSize(size[0], size[1])
self.w4.setFixedSize(size[0], size[1])
self.w5.setFixedSize(size[0], size[1])
self.w6.setFixedSize(size[0], size[1])
self.full_container = QVBoxLayout()
self.full_container.setContentsMargins(0,0,0,0)
self.radio_widget = QWidget()
self.radio_widget.setFixedSize(200, 50)
self.radio_container = QHBoxLayout()
self.radio_container.setContentsMargins(0,0,0,0)
self.widget_widget = QWidget()
self.widget_container = QVBoxLayout()
self.radio_widget.setLayout(self.radio_container)
self.widget_widget.setLayout(self.widget_container)
self.full_container.addWidget(self.radio_widget)
self.full_container.addWidget(self.widget_widget)
self.radio_container.addWidget(self.r1)
self.radio_container.addWidget(self.r2)
self.widget_container.addWidget(self.w1)
self.widget_container.addWidget(self.w2)
self.widget_container.addWidget(self.w3)
self.widget_container.addWidget(self.w4)
self.widget_container.addWidget(self.w5)
self.widget_container.addWidget(self.w6)
self.setLayout(self.full_container)
self.r1.setChecked(True)
def paintEvent(self, event):
if self.r1.isChecked():
self.w4.hide()
self.w5.hide()
self.w6.hide()
self.setFixedSize(self.small_size[0],self.small_size[1])
elif self.r2.isChecked():
self.w4.show()
self.w5.show()
self.w6.show()
self.setFixedSize(self.big_size[0], self.big_size[1])
self.full_container.update()
super(CustomDialog, self).paintEvent(event)
def run():
app = QApplication(sys.argv)
GUI = CustomDialog()
GUI.show()
sys.exit(app.exec_())
run()
Observe that I'm using PyQt5, so if you need you just gotta change your imports to the version you are using.

Related

Pyside6 shearing PDF file on window resize

I'm using QT (PySide) to view PDFs (using the PyMuPDF library) but when I resize I get a shearing artifact.
Like this:
Here is a minimal example:
import sys
import fitz
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QScrollArea
from PySide6.QtGui import QPixmap, QImage
from PySide6.QtCore import Qt
class PDFWindow(QMainWindow):
def __init__(self, filename):
super().__init__()
# Open the PDF and get the first page
self.pdf_doc = fitz.open(filename)
self.page = self.pdf_doc[0]
# Create a QLabel to hold the rendered page
self.label = QLabel()
self.label.setAlignment(Qt.AlignCenter)
# Create a QScrollArea to hold the label
self.scroll_area = QScrollArea()
self.scroll_area.setWidget(self.label)
self.scroll_area.setWidgetResizable(True)
self.setCentralWidget(self.scroll_area)
# Render the page and set it as the pixmap for the label
self.render_page()
# Connect the windowResized signal to the render_page function
self.resizeEvent = self.render_page
def render_page(self, event=None):
zoom = 1
if event is not None:
bound = self.page.bound()
page_width, page_height = bound.width, bound.height
zoom = min(event.size().width() / page_width,
event.size().height() / page_height)
pixmap = self.page.get_pixmap(matrix=fitz.Matrix(zoom, zoom),
colorspace='rgb')
image = QImage(pixmap.samples, pixmap.width, pixmap.height,
QImage.Format_RGB888)
cur_pixmap= QPixmap.fromImage(image)
self.label.setPixmap(cur_pixmap)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = PDFWindow("example.pdf")
window.show()
sys.exit(app.exec())

How do I prevent double valueChanged events when I press the arrows in a QSpinbox?

Could be a either a Qt bug, or a bug in my app, I am hoping someone can help me diagnose this.
I am building a PyQt5 application with an interface built using qtdesigner and pyuic5.
I have to QSpinBox widgets in my window, which I connect to two methods like this:
self.img1IndexBox.valueChanged.connect(self.changeImage1)
self.img2IndexBox.valueChanged.connect(self.changeImage2)
Everything seems to work fine in the application, except for one thing: If I scroll the mouse over the spinbox, I can make it increment the value. If I change the value with text, all works fine. If I use keyboard arrows on the spinbox, it works fine. But if I click on either the up or down arrows from the spinbox, I get get two changeValue events, a double increment. Like I clicked twice. In fact, it even looks from the animation that it is creating an event for the downpress, and another when the button goes back up.
Could this be just a library bug, or what could be causing this in my program? How could I debug this?
You might be able to prevent that double thing by setting spinbox enable to false.
then make it enable to true after processing large data.
Upon clicking the arrow up/down
on_valuechanged
ui->spinbox->setEnabled(false);
then set to true before the function on_valuechanged ends.
ui->spinbox->setEnabled(true);
Apparently the problem is the event is triggering a very long routine, this delays the "button release" event, and it is enough time to make the system think the user is actually holding the button, generating more events... But I would still be interested in learning what would be a good walk-around. Would there be a nice pyqt-onic way to start a thread for that method?
http://www.qtcentre.org/archive/index.php/t-43078.html
Instead of using valueChanged.connect use editingFinished.connect it will make sure the function is called only after value is provided.
PyQt
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())
PySide
import sys
from PySide6.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
from PySide6.QtGui import Qt
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())

I can't see QLabel and QLineEdit widgets in my QMainWindow in Python2

I wrote this code and I don't understand why widgets QLabel and QLineEdit don't show up? Do I have to put them in another class? It's Python2.7 and PySide.
This is how a window looks like when I run the code:
#!/usr/bin/env python
# coding: utf-8
import sys
import crypt
from PySide import QtGui
class MyApp(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.initui()
def initui(self):
# main window size, title and icon
self.setMinimumSize(500, 350)
self.setWindowTitle("Calculate a password hash in Linux")
# lines for entering data
self.saltLabel = QtGui.QLabel("Salt:")
self.saltLine = QtGui.QLineEdit()
self.saltLine.setPlaceholderText("e.g. $6$xxxxxxxx")
# set layout
grid = QtGui.QGridLayout()
grid.addWidget(self.saltLabel, 0, 0)
grid.addWidget(self.saltLine, 1, 0)
self.setLayout(grid)
# show a widget
self.show()
def main():
app = QtGui.QApplication(sys.argv)
instance = MyApp()
instance.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
How about using a QWidget as centralWidget
widget = QWidget()
widget.setLayout(grid)
#add your widgets and...
self.setCentralWidget(widget)
and you don't need to call show() since you do in your __main__
It's up to the owner but i would recommend sublassing a QWidget and leave your QMainWindow instance as concise as possible. An implementation could be:
class MyWidget(QtGui.QWidget):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
grid = QtGui.QGridLayout()
#and so on...
and use this as widget in your QMainWindow instance. This increases readability and maintainability and reusability a lot :)

QMovie setting filename second time wont work

I am setting the Initial Movie File like this:
self.movie = QMovie("dotGreenBlack.gif", QByteArray(), self)
At runtime, i want to change it by setting:
self.movie.setFileName("dotGreyStatic.gif")
But this leads to frame-freeze of the primarly set file "dotGreenBlack.gif"
Anything else i have to do to change the Gif-Animation on runtime?
Here is the fullcode:
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class ImagePlayer(QWidget):
def __init__(self, filename, title, parent=None):
QWidget.__init__(self, parent)
# Load the file into a QMovie
self.movie = QMovie(filename, QByteArray(), self)
size = self.movie.scaledSize()
self.setGeometry(200, 200, size.width(), size.height())
self.setWindowTitle(title)
self.movie_screen = QLabel()
# Make label fit the gif
self.movie_screen.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.movie_screen.setAlignment(Qt.AlignCenter)
# Create the layout
main_layout = QVBoxLayout()
main_layout.addWidget(self.movie_screen)
self.setLayout(main_layout)
# Add the QMovie object to the label
self.movie.setCacheMode(QMovie.CacheAll)
self.movie.setSpeed(100)
self.movie_screen.setMovie(self.movie)
self.movie.start()
self.movie.loopCount()
self.movie.setFileName("dotGreyStatic.gif")
self.movie_screen.setMovie(self.movie)
if __name__ == "__main__":
gif = "dotGreenBlack.gif"
app = QApplication(sys.argv)
app.setStyleSheet("QWidget { background-color: black }")
player = ImagePlayer(gif, "was")
player.show()
sys.exit(app.exec_())
Gif Icons used in this Example:
You just need to stop and restart the movie:
self.movie.stop()
self.movie.setFileName("dotGreyStatic.gif")
self.movie.start()

What class should I use to create painting application by Qt?

It is second time to post this question. Because first one didn't show any effort.
I want to create simple painting application which consist with just black/white pen and canvas. So to create my painting app I want to know what class should I use. Or is there painting soft which is open source and created by Qt?
I know there is QPainter class. So using QPainter is the right way? I heard it is low level graphical tool. So is there more useful one? And I think following source code is one of the implementation of painter app. But Is this right way? I think there is more good way.
My code:
https://gist.github.com/keimina/469fa17508ae2c0c90c4#file-simplepaintapp-py
from PySide.QtGui import QApplication, QMainWindow, QAction, QActionGroup, QWidget, QCursor, QPainter
from PySide.QtCore import QTimer
import PySide.QtCore as QtCore
class W(QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(400,400)
self.myIsMousePressing = False
self.p = QPainter(self)
self.autoFillBackground()
self.x = 0
self.y = 0
self.r = dict()#{(x,Y,49, 49):rect}
self.penColor = 1
def mousePressEvent(self, event):
self.myIsMousePressing = True
def mouseReleaseEvent(self, event):
self.myIsMousePressing = False
def myTimeOut(self):
if self.myIsMousePressing:
pos = self.mapFromGlobal(QCursor.pos())
self.x = pos.x()/50
self.y = pos.y()/50
self.r[(self.x*50, self.y*50, 49, 49)] = self.penColor
def paintEvent(self, event):
self.p.begin(self)
for k in self.r.keys():
if self.r[k] == 1:
self.p.setPen(QtCore.Qt.black)
self.p.setBrush(QtCore.Qt.black)
else:
self.p.setPen(QtCore.Qt.white)
self.p.setBrush(QtCore.Qt.white)
self.p.drawRect(*k)
self.p.end()
self.update()
class MyWidget(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(400, 400)
self.initMenu()
self.w = W()
self.setCentralWidget(self.w)
self.t = QTimer(self.w)
self.t.timeout.connect(self.w.myTimeOut)
self.t.start(1)
def initMenu(self):
self.fileMenu = self.menuBar().addMenu("&File")
self.editMenu = self.menuBar().addMenu("&Edit")
self.helpMenu = self.menuBar().addMenu("&Help")
self.fileMenuAction = QAction("&New", self)
self.editMenuAction1 = QAction("&Black", self)
self.editMenuAction2 = QAction("&White", self)
self.helpMenuAction = QAction("&About", self)
actGroup = QActionGroup(self)
actGroup.addAction(self.editMenuAction1)
actGroup.addAction(self.editMenuAction2)
self.editMenuAction1.setCheckable(True)
self.editMenuAction2.setCheckable(True)
self.editMenuAction1.setChecked(True)
self.fileMenu.addAction(self.fileMenuAction)
self.editMenu.addAction(self.editMenuAction1)
self.editMenu.addAction(self.editMenuAction2)
self.helpMenu.addAction(self.helpMenuAction)
self.editMenuAction1.triggered.connect(self.action1)
self.editMenuAction2.triggered.connect(self.action2)
def action1(self):
self.w.penColor = 1
def action2(self):
self.w.penColor = 2
app = QApplication([])
mainWin = MyWidget()
mainWin.show()
app.exec_()
Thanks.
P.S. I'm using PySide but any other Qt is OK.
QPainter is essentially the only way, short of manipulating individual pixels in a QImage, or using OpenGL, to paint something in Qt. So its use goes without saying and if you paint in Qt, you will have to use QPainter. That's how painting is done. But this has nothing to do with an application that a human might use to "paint". The painting we're talking about is what your application has to do to show something to the user, no matter what the user is doing.
What you're asking is if there's something application-specific in Qt that would help with implementing a "drawing" application. If you're after a vector drawing application, then the graphics scene framework might be of use. Otherwise, there's nothing to help you. You'll have to start with a plain QWidget and implement the behavior you need.

Resources