pyqt5 tabwidget vertical tab horizontal text alignment left - python

Since pyqt doesn't have horizontal text in vertical tab option, I followed this link to make it happen.
I wanted to have icons on the left and then text after icon and different color for selected tab text, inactive tabs text. Below code gets it done almost. The only problem is text alignment is center. I tried changing tabRect.center() but changing it with left and top or right etc is making it crash.
The commented code which I got from this linkgets me left alignment but it didn't have icons which I added. But in that I am unable to change text color of inactive tabs.
I am new to python and I am unable to find a solution for this. I tried this as well link but this only sets background color. tried using this option as well setTabTextColor link but it didn't work for some reason. been trying from 2 days.
Whenever I try to set text color using stylesheet with commented code, the "color" option won't work in stylesheet. any ideas on how to get this done? thanks
from PyQt5 import QtCore, QtGui, QtWidgets
class TabBar(QtWidgets.QTabBar):
def tabSizeHint(self, index):
s = QtWidgets.QTabBar.tabSizeHint(self, index)
s.transpose()
return s
def paintEvent(self, event):
painter = QtWidgets.QStylePainter(self)
opt = QtWidgets.QStyleOptionTab()
for i in range(self.count()):
self.initStyleOption(opt, i)
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, opt)
painter.save()
s = opt.rect.size()
s.transpose()
r = QtCore.QRect(QtCore.QPoint(), s)
r.moveCenter(opt.rect.center())
opt.rect = r
c = self.tabRect(i).center()
painter.translate(c)
painter.rotate(90)
painter.translate(-c)
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabLabel, opt)
painter.restore()
# for i in range(self.count()):
# self.initStyleOption(opt, i)
# c = self.tabRect(i)
# c.moveLeft(35)
# painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, opt)
# # painter.setPen(QColor(255, 255, 255))
# painter.drawText(c, QtCore.Qt.AlignVCenter | QtCore.Qt.TextDontClip, self.tabText(i))
# if i == 0:
# painter.drawImage(QtCore.QRectF(8, 8, 20, 20), QtGui.QImage("images/logo.png"))
# if i == 1:
# painter.drawImage(QtCore.QRectF(8, 44, 20, 20), QtGui.QImage("images/data.png"))
# if i == 2:
# painter.drawImage(QtCore.QRectF(8, 82, 20, 20), QtGui.QImage("images/browse.png"))
# if i == 3:
# painter.drawImage(QtCore.QRectF(8, 120, 20, 20), QtGui.QImage("images/off.png"))
# if i == 4:
# painter.drawImage(QtCore.QRectF(8, 158, 20, 20), QtGui.QImage("images/cal.png"))
# if i == 5:
# painter.drawImage(QtCore.QRectF(8, 196, 20, 20), QtGui.QImage("images/fol.png"))
# if i == 6:
# painter.drawImage(QtCore.QRectF(8, 232, 20, 20), QtGui.QImage("images/exc.png"))
# painter.end()
class TabWidget(QtWidgets.QTabWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QTabWidget.__init__(self, *args, **kwargs)
self.setTabBar(TabBar(self))
self.setTabPosition(QtWidgets.QTabWidget.West)

The solution is to use a QProxyStyle to redirect text painting:
from PyQt5 import QtCore, QtGui, QtWidgets
class TabBar(QtWidgets.QTabBar):
def tabSizeHint(self, index):
s = QtWidgets.QTabBar.tabSizeHint(self, index)
s.transpose()
return s
def paintEvent(self, event):
painter = QtWidgets.QStylePainter(self)
opt = QtWidgets.QStyleOptionTab()
for i in range(self.count()):
self.initStyleOption(opt, i)
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, opt)
painter.save()
s = opt.rect.size()
s.transpose()
r = QtCore.QRect(QtCore.QPoint(), s)
r.moveCenter(opt.rect.center())
opt.rect = r
c = self.tabRect(i).center()
painter.translate(c)
painter.rotate(90)
painter.translate(-c)
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabLabel, opt);
painter.restore()
class TabWidget(QtWidgets.QTabWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QTabWidget.__init__(self, *args, **kwargs)
self.setTabBar(TabBar(self))
self.setTabPosition(QtWidgets.QTabWidget.West)
class ProxyStyle(QtWidgets.QProxyStyle):
def drawControl(self, element, opt, painter, widget):
if element == QtWidgets.QStyle.CE_TabBarTabLabel:
ic = self.pixelMetric(QtWidgets.QStyle.PM_TabBarIconSize)
r = QtCore.QRect(opt.rect)
w = 0 if opt.icon.isNull() else opt.rect.width() + self.pixelMetric(QtWidgets.QStyle.PM_TabBarIconSize)
r.setHeight(opt.fontMetrics.width(opt.text) + w)
r.moveBottom(opt.rect.bottom())
opt.rect = r
QtWidgets.QProxyStyle.drawControl(self, element, opt, painter, widget)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
QtWidgets.QApplication.setStyle(ProxyStyle())
w = TabWidget()
w.addTab(QtWidgets.QWidget(), QtGui.QIcon("zoom.png"), "ABC")
w.addTab(QtWidgets.QWidget(), QtGui.QIcon("zoom-in.png"), "ABCDEFGH")
w.addTab(QtWidgets.QWidget(), QtGui.QIcon("zoom-out.png"), "XYZ")
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

In case you have previously designed any tab in QtDesigner.
For example, I have designed this tab in QtDesigner:
Now I have generated the py file from the ui file, and the UI file, looks like this:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'tabwidget.ui'
#
# Created by: PyQt5 UI code generator 5.13.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab)
self.gridLayout_2.setObjectName("gridLayout_2")
self.pushButton_reach = QtWidgets.QPushButton(self.tab)
self.pushButton_reach.setObjectName("pushButton_reach")
self.gridLayout_2.addWidget(self.pushButton_reach, 1, 1, 1, 1)
self.label = QtWidgets.QLabel(self.tab)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.gridLayout_2.addWidget(self.label, 0, 1, 1, 1)
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.tabWidget.addTab(self.tab_2, "")
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
self.pushButton_addoptions = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_addoptions.setObjectName("pushButton_addoptions")
self.gridLayout.addWidget(self.pushButton_addoptions, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton_reach.setText(_translate("MainWindow", "I am here"))
self.label.setText(_translate("MainWindow", "Gotcha!"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "First Tab"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Second Tab"))
self.pushButton_addoptions.setText(_translate("MainWindow", "ADD options"))
And from eyllanesc's answer I figured out how to add tabs vertically and I added the pre-existing First-Tab to my modified tabwidget as follows:
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5 import QtGui, QtWidgets, QtCore
import tabwidget
# new additions
class TabBar(QtWidgets.QTabBar):
def tabSizeHint(self, index):
s = QtWidgets.QTabBar.tabSizeHint(self, index)
s.transpose()
return s
def paintEvent(self, event):
painter = QtWidgets.QStylePainter(self)
opt = QtWidgets.QStyleOptionTab()
for i in range(self.count()):
self.initStyleOption(opt, i)
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, opt)
painter.save()
s = opt.rect.size()
s.transpose()
r = QtCore.QRect(QtCore.QPoint(), s)
r.moveCenter(opt.rect.center())
opt.rect = r
c = self.tabRect(i).center()
painter.translate(c)
painter.rotate(90)
painter.translate(-c)
painter.drawControl(QtWidgets.QStyle.CE_TabBarTabLabel, opt)
painter.restore()
# class TabWidget(QtWidgets.QTabWidget):
# def __init__(self, *args, **kwargs):
# QtWidgets.QTabWidget.__init__(self, *args, **kwargs)
# self.setTabBar(TabBar(self))
# self.setTabPosition(QtWidgets.QTabWidget.West)
# new additions
class app_window(QMainWindow):
def __init__(self):
super().__init__()
self.ui = tabwidget.Ui_MainWindow()
self.ui.setupUi(self)
# self.ui.tabWidget = TabWidget()
self.ui.tabWidget.setTabBar(TabBar(self.ui.tabWidget))
self.ui.tabWidget.setTabPosition(self.ui.tabWidget.West)
self.ui.tabWidget.insertTab(0, self.ui.tab, "My tab")
self.ui.pushButton_reach.clicked.connect(self.display)
self.show()
def display(self):
print("reached")
self.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = app_window()
w.show()
sys.exit(app.exec_())
Now my windows look like this:
I hope this will clear many people's problem who directly just want to recreate their tabwidget vertically!

I couldn't get working the first answer with only text (not icons) because my text get cut.
And even if I was able to run the second answer I think it isn't clear.
So here is my answer:
This code defines VerticalTabWidget class using second answer's code:
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtGui, QtWidgets, QtCore
class TabBar(QTabBar):
def tabSizeHint(self, index):
s = QTabBar.tabSizeHint(self, index)
s.transpose()
return s
def paintEvent(self, event):
painter = QStylePainter(self)
opt = QStyleOptionTab()
for i in range(self.count()):
self.initStyleOption(opt, i)
painter.drawControl(QStyle.CE_TabBarTabShape, opt)
painter.save()
s = opt.rect.size()
s.transpose()
r = QtCore.QRect(QtCore.QPoint(), s)
r.moveCenter(opt.rect.center())
opt.rect = r
c = self.tabRect(i).center()
painter.translate(c)
painter.rotate(90)
painter.translate(-c)
painter.drawControl(QStyle.CE_TabBarTabLabel, opt)
painter.restore()
class VerticalTabWidget(QTabWidget):
def __init__(self, *args, **kwargs):
QTabWidget.__init__(self, *args, **kwargs)
self.setTabBar(TabBar())
self.setTabPosition(QtWidgets.QTabWidget.West)
class app_window(QMainWindow):
def __init__(self):
super().__init__()
tabs = VerticalTabWidget()
tabs.addTab(QWidget(), "First Tab")
tabs.addTab(QWidget(), "Second Tab")
tabs.addTab(QWidget(), "Third Tab")
self.setCentralWidget(tabs)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = app_window()
w.show()
sys.exit(app.exec_())
You can just copy the TabBar and VerticalTabWidget classes into your code and use VerticalTabWidget as any QTabWidget.

Related

Remove QCombobox to show all the data in the window panel for the user to select multiple values at once

I have used the following code what it does is displays a list of values in the combo box but the difficulty that i am experiencing is that each and every time the value is check ed the drop down closes.
Is there a way possible where instead of the drop down all the menu is displayed in the main window as a list of all check boxes so that multiple values can be clicked at once.
Following is the code snippet.
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
class CheckableComboBox(QtWidgets.QComboBox):
def __init__(self, parent = None):
super(CheckableComboBox, self).__init__(parent)
self.setView(QtWidgets.QListView(self))
self.view().pressed.connect(self.handleItemPressed)
self.setModel(QtGui.QStandardItemModel(self))
def handleItemPressed(self, index):
item = self.model().itemFromIndex(index)
if item.checkState() == QtCore.Qt.Checked:
item.setCheckState(QtCore.Qt.Unchecked)
else:
item.setCheckState(QtCore.Qt.Checked)
def checkedItems(self):
checkedItems = []
for index in range(self.count()):
item = self.model().item(index)
if item.checkState() == QtCore.Qt.Checked:
checkedItems.append(item)
return checkedItems
class Ui_dialogCreateBatch(object):
def setupUi(self, dialogCreateBatch):
dialogCreateBatch.resize(400, 338)
dialogCreateBatch.setMouseTracking(True)
self.gridLayoutWidget = QtWidgets.QWidget(dialogCreateBatch)
self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 10, 360, 115))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.cboItemList = CheckableComboBox(self.gridLayoutWidget)
self.cboItemList.setObjectName("cboItemList")
self.gridLayout.addWidget(self.cboItemList, 0, 0, 1, 1)
data = ('item1', 'item2', 'item3')
for index, element in enumerate(data):
self.cboItemList.addItem(element)
item = self.cboItemList.model().item(index, 0)
item.setCheckState(QtCore.Qt.Unchecked)
self.buttonBox = QtWidgets.QDialogButtonBox(dialogCreateBatch)
self.buttonBox.setGeometry(QtCore.QRect(100, 300, 156, 23))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.retranslateUi(dialogCreateBatch)
QtCore.QMetaObject.connectSlotsByName(dialogCreateBatch)
def retranslateUi(self, dialogCreateBatch):
_translate = QtCore.QCoreApplication.translate
dialogCreateBatch.setWindowTitle(_translate("dialogCreateBatch", "Create Item Batch"))
class DialogCreateBatch(QtWidgets.QDialog, Ui_dialogCreateBatch):
def __init__(self, parent=None):
QtWidgets.QDialog.__init__(self, parent)
self.setupUi(self)
self.buttonBox.accepted.connect(self.on_accepted)
self.buttonBox.rejected.connect(self.reject)
def on_accepted(self):
selectedItems = self.cboItemList.checkedItems()
print(selectedItems)
self.accept()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = DialogCreateBatch()
w.show()
sys.exit(app.exec_())
The following code implements a QDialog that shows a QListView with a model that has checkable items based on QStandardItemModel:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class DialogCreateBatch(QtWidgets.QDialog):
def __init__(self, parent=None):
super(DialogCreateBatch, self).__init__(parent)
self.model = QtGui.QStandardItemModel(self)
self.view = QtWidgets.QListView()
self.view.setModel(self.model)
self.button_box = QtWidgets.QDialogButtonBox()
self.button_box.setOrientation(QtCore.Qt.Horizontal)
self.button_box.setStandardButtons(
QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok
)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.view)
lay.addWidget(self.button_box)
#property
def items(self):
items = []
for i in range(self.model.rowCount()):
it = self.model.item(i)
items.append(it.text())
return items
#items.setter
def items(self, items):
self.model.clear()
for item in items:
it = QtGui.QStandardItem(item)
it.setCheckable(True)
self.model.appendRow(it)
#property
def checked_items(self):
checked_items = []
for i in range(self.model.rowCount()):
it = self.model.item(i)
if it.data(QtCore.Qt.CheckStateRole) == QtCore.Qt.Checked:
checked_items.append(it.text())
return checked_items
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = DialogCreateBatch()
w.setWindowTitle("Stack Overflow")
w.items = ("item1", "item2", "item3")
if w.exec_() == QtWidgets.QDialog.Accepted:
print(w.checked_items)

PySide2 composite widget Hover Effect

How to animate position,scale or any other attributes of composed elements inside custom widget, on mouse pointer enters or leaves the QListWidgetItem ?
(see reference image below)
And is there any better way to manage space around ListWidgetItem ?
item_widget.sizeHint() gives unwanted extra space, which is why i added hard coded value to setSizeHint.
ProductMainWindow.py
from PySide2 import QtCore, QtGui, QtWidgets
import productThumbnailWidget
import sys
sys.path.append('E:/code')
class prodMainWindowUI(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.productListWidget = QtWidgets.QListWidget(self.centralwidget)
self.productListWidget.setObjectName("productListWidget")
self.productListWidget.setViewMode(QtWidgets.QListWidget.IconMode)
self.productListWidget.setIconSize(QtCore.QSize(256,256))
self.productListWidget.setResizeMode(QtWidgets.QListWidget.Adjust)
self.productListWidget.setMovement(QtWidgets.QListWidget.Static) # disable drag and drop
self.productListWidget.setGridSize(QtCore.QSize(256 + 5, 256 + 5))
self.verticalLayout.addWidget(self.productListWidget)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
for i in range(6):
item = QtWidgets.QListWidgetItem(self.productListWidget)
item_widget = productThumbnailWidget.productThumbWidget()
#item.setSizeHint(item_widget.sizeHint())
item.setSizeHint(QtCore.QSize(256,256))
self.productListWidget.addItem(item)
self.productListWidget.setItemWidget(item, item_widget)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "MainWindow", None, -1))
app = QtWidgets.QApplication(sys.argv)
prodUI = prodMainWindowUI()
prodUI.show()
sys.exit(app.exec_())
productThumbnailWidget
from PySide2 import QtCore, QtGui, QtWidgets
class productThumbWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(productThumbWidget, self).__init__(parent)
self.setObjectName("Form")
self.resize(256, 256)
self.setMinimumSize(QtCore.QSize(256, 256))
self.setMaximumSize(QtCore.QSize(256, 256))
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.frame = QtWidgets.QFrame()
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.thumbnailLabel = QtWidgets.QLabel("", self.frame)
self.thumbnailLabel.setObjectName("thumbnailLabel")
self.thumbnailLabel.setScaledContents(False)
self.thumbnailLabel.setGeometry(QtCore.QRect(0, 0, 256, 256))
self.thumbnailLabel.setMinimumSize(QtCore.QSize(256, 256))
self.thumbnailLabel.setMaximumSize(QtCore.QSize(256, 256))
self.thumbnailLabel.setPixmap(QtGui.QPixmap("E:/code/android.png"))
self.backgroundLabel = QtWidgets.QLabel("", self.frame)
self.backgroundLabel.setObjectName("backgroundLabel")
self.backgroundLabel.setGeometry(QtCore.QRect(0, 206, 256, 50))
self.backgroundLabel.setMinimumSize(QtCore.QSize(256, 50))
self.backgroundLabel.setMaximumSize(QtCore.QSize(256, 50))
self.backgroundLabel.setStyleSheet("QLabel#backgroundLabel{\n"
" background: #32353B;\n"
"}")
self.titleLabel = QtWidgets.QLabel("Title", self.frame)
self.titleLabel.setObjectName("titleLabel")
self.titleLabel.setGeometry(QtCore.QRect(10, 218, 246, 25))
self.titleLabel.setMinimumSize(QtCore.QSize(246, 25))
self.titleLabel.setMaximumSize(QtCore.QSize(246, 25))
font = QtGui.QFont()
font.setFamily("SF Pro Display")
font.setPointSize(16)
font.setWeight(75)
font.setBold(True)
self.titleLabel.setFont(font)
self.titleLabel.setStyleSheet("QLabel#titleLabel {\n"
" color: #FFFFFF;\n"
"}")
self.verticalLayout.addWidget(self.frame)
self.setLayout(self.verticalLayout)
Output
The solution is to animate the position of the rectangle using QPropertyAnimation, and to activate the animations using the events QEvent::Enter and QEvent::Leave:
import shiboken2
from PySide2 import QtCore, QtGui, QtWidgets
class RectangleHoverEffect(QtCore.QObject):
def __init__(self, rectangle, parent):
super().__init__(parent)
if not isinstance(rectangle, QtWidgets.QWidget):
raise TypeError(f"{rectangle} must be a QWidget")
if rectangle.parent() is None:
raise ValueError(f"{rectangle} must have a parent")
self.m_rectangle = rectangle
self.m_rectangle.parent().installEventFilter(self)
self.m_animation = QtCore.QPropertyAnimation(
self,
targetObject=self.m_rectangle,
propertyName=b"pos",
duration=300,
easingCurve=QtCore.QEasingCurve.OutQuad,
)
def eventFilter(self, obj, event):
if shiboken2.isValid(self.m_rectangle):
if self.m_rectangle.parent() is obj:
y0 = self.m_rectangle.parent().height()
y1 = self.m_rectangle.parent().height() - self.m_rectangle.height()
if event.type() == QtCore.QEvent.Enter:
self._start_animation(y0, y1)
elif event.type() == QtCore.QEvent.Leave:
self._start_animation(y1, y0)
return super().eventFilter(obj, event)
def _start_animation(self, y0, y1):
self.m_animation.setStartValue(QtCore.QPoint(0, y0))
self.m_animation.setEndValue(QtCore.QPoint(0, y1))
self.m_animation.start()
class ThumbWidget(QtWidgets.QFrame):
def __init__(self, title, pixmap, parent=None):
super().__init__(parent)
self.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.setFrameShadow(QtWidgets.QFrame.Raised)
pixmap_label = QtWidgets.QLabel(pixmap=pixmap, scaledContents=True)
title_label = QtWidgets.QLabel(title)
title_label.setStyleSheet("""color: #FFFFFF""")
font = QtGui.QFont()
font.setFamily("SF Pro Display")
font.setPointSize(16)
font.setWeight(75)
font.setBold(True)
title_label.setFont(font)
background_label = QtWidgets.QLabel(pixmap_label)
background_label.setStyleSheet("background: #32353B;")
background_label.setFixedSize(self.width(), 50)
background_label.move(0, self.height())
background_lay = QtWidgets.QVBoxLayout(background_label)
background_lay.addWidget(title_label)
self.setFixedSize(256, 256)
lay = QtWidgets.QVBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
lay.setSpacing(0)
lay.addWidget(pixmap_label)
effect = RectangleHoverEffect(background_label, self)
class ProductMainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.product_listwidget = QtWidgets.QListWidget(
viewMode=QtWidgets.QListWidget.IconMode,
iconSize=QtCore.QSize(256, 256),
resizeMode=QtWidgets.QListWidget.Adjust,
movement=QtWidgets.QListWidget.Static,
)
self.product_listwidget.setGridSize(QtCore.QSize(256 + 5, 256 + 5))
for i in range(6):
item = QtWidgets.QListWidgetItem()
item_widget = ThumbWidget(f"Title {i}", QtGui.QPixmap("E:/code/android.png"))
item.setSizeHint(QtCore.QSize(256, 256))
self.product_listwidget.addItem(item)
self.product_listwidget.setItemWidget(item, item_widget)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
lay.addWidget(self.product_listwidget)
self.resize(960, 480)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = ProductMainWindow()
w.show()
sys.exit(app.exec_())

Putting cursor in QLineEdit

I would like to put a cursor in a blank line edit box without having to click on it.
I looked through the Reference here
http://ftp.ics.uci.edu/pub/centos0/ics-custom-build/BUILD/PyQt-x11-gpl-4.7.2/doc/html/qlineedit.html#selectionStart
I tried calling
QLineEdit.home(True)
But this did not select the lineEdit so to say.
here is a watered down version of the code
from PyQt4 import QtCore, QtGui
import sys
import os
import os.path
class Ui_Form1(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setupUi(self)
def setupUi(self, Form):
#init stuff
Form.setObjectName("Form")
Form.resize(794, 538)
self.gridLayout = QtGui.QGridLayout(Form)
self.gridLayout.setObjectName("gridLayout")
self.hLayout = QtGui.QHBoxLayout(Form)
self.hLayout.setObjectName("hLayout")
self.vLayout = QtGui.QVBoxLayout(Form)
self.vLayout.setObjectName("vLayout")
#label for information
self.gridLayout.addLayout(self.hLayout, 0, 0)
self.hLayout.addLayout(self.vLayout, 0)
self.label = QtGui.QLabel(Form)
self.label.setObjectName("label")
#label pixmap
self.label2 = QtGui.QLabel(Form)
self.label2.setObjectName("label")
#line edit
self.lineEdit = QtGui.QLineEdit(Form)
self.lineEdit.setAlignment(QtCore.Qt.AlignCenter)
self.gridLayout.addWidget(self.lineEdit, 3,0)
self.list = QtGui.QListWidget(self)
self.list.setObjectName("outlist")
self.list.setMinimumHeight(150)
self.vLayout.addWidget(self.list, 1)
self.hLayout.addWidget(self.label, 1)
self.vLayout.addWidget(self.label2, 0)
#self.hLayout.addWidget(self.label2, 0)
self.label2.setText('line edit')
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setText('Picture would go here')
self.label2.setText('line edit')
self.list.addItem('cursor will disappear when this is pressed')
#press enter to update pic
#self.lineEdit.returnPressed.connect(self.update_pic)
#####sh
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ex = Ui_Form1()
ex.show()
sys.exit(app.exec_())
self.lineEdit.setFocus(QtCore.Qt.StrongFocus)
In PyQt5:
self.lineEdit.setFocus()

Changing the color of a QGraphicsItem

I am working on a simple software, which has a GUI. (I'm using PyQt)
I have 2 radiobuttons. If the first is selected, then by clicking on the Graphicsscene a GraphicsItem will be added to the scene.
I would like to have a button, which would change the color of these points by pressing it. The color doesn't matter. It could be red for example. How could I do that? Thank you!
For placing the widgets I used the Qt Designer, and then created a subclass called SimpleWindow.
Here is the code:
The points class:
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtWidgets import QGraphicsItem
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 4, 4)
self.x=x
self.y=y
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, Qt.black)
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QPen, QBrush
from PyQt5.QtWidgets import QGraphicsScene
The scene:
class PointsGraphicsScene(QGraphicsScene):
def __init__(self, parent=None):
QGraphicsScene.__init__(self, parent)
self.setSceneRect(0, 0, 200, 200)
self.opt = ""
def setOption(self, opt):
self.opt = opt
def mousePressEvent(self, event):
pen = QPen(QtCore.Qt.black)
brush = QBrush(QtCore.Qt.black)
x = event.scenePos().x()
y = event.scenePos().y()
if self.opt == "Generate":
p = point.Point(x, y)
p.setPos(x, y)
self.addItem(p)
elif self.opt == "Select":
print(x, y)
The dialog window:
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QButtonGroup
import window
from scene import PointsGraphicsScene
class SimpleWindow(QtWidgets.QMainWindow, window.Ui_Dialog):
def __init__(self, parent=None):
super(SimpleWindow, self).__init__(parent)
self.setupUi(self)
self.scene = PointsGraphicsScene(self)
self.graphicsView.setScene(self.scene)
self.graphicsView.setAlignment(QtCore.Qt.AlignLeft |
QtCore.Qt.AlignTop)
group = QButtonGroup(self)
group.addButton(self.radioButton)
group.addButton(self.radioButton_2)
group.buttonClicked.connect(lambda btn:
self.scene.setOption(btn.text()))
self.radioButton.setChecked(True)
self.scene.setOption(self.radioButton.text())
app = QtWidgets.QApplication(sys.argv)
form = SimpleWindow()
form.show()
app.exec_()
This is the the class generated by the Designer.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(538, 269)
self.graphicsView = QtWidgets.QGraphicsView(Dialog)
self.graphicsView.setGeometry(QtCore.QRect(130, 10, 371, 221))
self.graphicsView.setObjectName("graphicsView")
self.radioButton = QtWidgets.QRadioButton(Dialog)
self.radioButton.setGeometry(QtCore.QRect(20, 30, 82, 31))
self.radioButton.setObjectName("radioButton")
self.radioButton_2 = QtWidgets.QRadioButton(Dialog)
self.radioButton_2.setGeometry(QtCore.QRect(20, 80, 82, 17))
self.radioButton_2.setObjectName("radioButton_2")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.radioButton.setText(_translate("Dialog", "Generate"))
self.radioButton_2.setText(_translate("Dialog", "Select"))
The first thing is to create a method that changes the color, in this case we will call it setBrush():
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 4, 4)
self.x=x
self.y=y
self._brush = QBrush(Qt.black)
def setBrush(self, brush):
self._brush = brush
self.update()
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)
The second thing to do is create the button and locate it in some position. then the QPushButton clicked signal is connected to some slot. Then we get the items of the scene through the items() method and change the color with setBrush():
class SimpleWindow(QtWidgets.QMainWindow, window.Ui_Dialog):
def __init__(self, parent=None):
super(SimpleWindow, self).__init__(parent)
self.setupUi(self)
self.scene = PointsGraphicsScene(self)
self.graphicsView.setScene(self.scene)
self.graphicsView.setAlignment(QtCore.Qt.AlignLeft |
QtCore.Qt.AlignTop)
group = QButtonGroup(self)
group.addButton(self.radioButton)
group.addButton(self.radioButton_2)
group.buttonClicked.connect(lambda btn:
self.scene.setOption(btn.text()))
self.radioButton.setChecked(True)
self.scene.setOption(self.radioButton.text())
button = QPushButton("change color", self)
button.move(20, 140)
button.clicked.connect(self.onClicked)
def onClicked(self):
for item in self.scene.items():
item.setBrush(QColor("red"))
app = QtWidgets.QApplication(sys.argv)
form = SimpleWindow()
form.show()
app.exec_()

Close and get data from a custom dialog PyQT5

I can't get to work a custom dialog made in Qt Designer. I understand how can i popup the dialog but i can't get the data from that line text.
main program.py
from PyQt5 import QtCore, QtGui, QtWidgets
from ui import Ui_MainWindow
from addui import Ui_Dialog as Form
from bs4 import BeautifulSoup
import requests
import time
import sys
class MainDialog(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)
self.actionRefresh.triggered.connect(self.refresh_btn)
self.actionAdd.triggered.connect(self.add_btn)
self.actionRemove.triggered.connect(self.remove_btn)
self.actionSettings.triggered.connect(self.settings_btn)
self.actionAbout.triggered.connect(self.about_btn)
self.actionExit.triggered.connect(self.exit_btn)
def open_dialog(self):
dialog = QtWidgets.QDialog()
dialog.ui = Form()
dialog.ui.setupUi(dialog)
dialog.exec_()
dialog.show()
def refresh_btn(self):
print('Refresh')
self.getting_data()
def add_btn(self):
print('Add')
self.open_dialog()
def remove_btn(self):
print('Remove')
def settings_btn(self):
print('Settings')
QtWidgets.QMessageBox.warning(self, 'Settings',
'Work in progress.\n'
' Coming soon!')
def about_btn(self):
print('About')
QtWidgets.QMessageBox.about(self, 'About Checking Prices',
'Checking Prices - Beta v1.0\n'
'\n'
'Copyright(c) 2015 - Pifu Valentin')
def exit_btn(self):
self.close()
def getting_data(self):
links = ['link1',
'link2',
'link3'
]
self.statusBar.showMessage('Getting data...')
try:
for nr, link in enumerate(links, start=1):
cont = requests.get(link)
soup = BeautifulSoup(cont.content, "html.parser")
title = soup.title.text[:40]
price = soup.find('span', {'itemprop': 'price'}).text
linetxt = ('{}. {} >> ${}').format(nr, title, price)
if nr == 1:
self.linetxt1.setText(linetxt)
elif nr == 2:
self.linetxt2.setText(linetxt)
elif nr == 3:
self.linetxt3.setText(linetxt)
self.statusBar.showMessage('Last updated - '+time.strftime('%H:%M:%S'))
except:
self.statusBar.showMessage('Error getting data.')
def main():
app = QtWidgets.QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
if __name__ == '__main__':
main()
addui.py (dialog)
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(200, 71)
Dialog.setMinimumSize(QtCore.QSize(200, 71))
Dialog.setMaximumSize(QtCore.QSize(200, 71))
Dialog.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("Icons/Plus-32.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
Dialog.setWindowIcon(icon)
self.gridLayout = QtWidgets.QGridLayout(Dialog)
self.gridLayout.setObjectName("gridLayout")
self.text_link = QtWidgets.QLineEdit(Dialog)
self.text_link.setObjectName("text_link")
self.gridLayout.addWidget(self.text_link, 0, 0, 1, 2)
self.add_link = QtWidgets.QPushButton(Dialog)
self.add_link.setObjectName("add_link")
self.gridLayout.addWidget(self.add_link, 1, 0, 1, 1)
self.cancel_link = QtWidgets.QPushButton(Dialog)
self.cancel_link.setObjectName("cancel_link")
self.gridLayout.addWidget(self.cancel_link, 1, 1, 1, 1)
self.retranslateUi(Dialog)
self.cancel_link.clicked.connect(self.exit_dialog)
self.add_link.clicked.connect(self.get_link)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Add link"))
self.add_link.setText(_translate("Dialog", "Add"))
self.cancel_link.setText(_translate("Dialog", "Cancel"))
def get_link(self):
print(self.text_link.text())
x = self.text_link.text()
return x
def exit_dialog(self):
self.destroy()
I have some problems with this program.
If i click on cancel to exit only the dialog not the main program. (i tried with self.close, self.hide...)
I want to add a link on that line_text and to get that link to the main program, but when i click on add to close dialog and data pass to the main program.
It's ok how i call the dialog ?
def open_dialog(self):
dialog = QtWidgets.QDialog()
dialog.ui = Form()
dialog.ui.setupUi(dialog)
dialog.exec_()
dialog.show()
Thanks. I don't get how i can do this, hope someone can help.
THanks again.
You need to exec_() to do this and Here is a minimal version of your code which is fixed
from PyQt4 import QtCore, QtGui
import sys
class PopUpDLG(QtGui.QDialog):
def __init__(self):
super(PopUpDLG, self).__init__()
self.setObjectName("self")
self.resize(200, 71)
self.setMinimumSize(QtCore.QSize(200, 71))
self.setMaximumSize(QtCore.QSize(200, 71))
self.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("Icons/Plus-32.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.setWindowIcon(icon)
self.gridLayout = QtGui.QGridLayout(self)
self.gridLayout.setObjectName("gridLayout")
self.text_link = QtGui.QLineEdit(self)
self.text_link.setObjectName("text_link")
self.gridLayout.addWidget(self.text_link, 0, 0, 1, 2)
self.add_link = QtGui.QPushButton(self)
self.add_link.setObjectName("add_link")
self.gridLayout.addWidget(self.add_link, 1, 0, 1, 1)
self.cancel_link = QtGui.QPushButton(self)
self.cancel_link.setObjectName("cancel_link")
self.gridLayout.addWidget(self.cancel_link, 1, 1, 1, 1)
self.retranslateUi(self)
self.cancel_link.clicked.connect(self.reject)
self.add_link.clicked.connect(self.get_link)
self.retrunVal = None
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
self.setWindowTitle(_translate("Dialog", "Add link"))
self.add_link.setText(_translate("Dialog", "Add"))
self.cancel_link.setText(_translate("Dialog", "Cancel"))
def get_link(self):
self.retrunVal = self.text_link.text()
self.accept()
def exec_(self):
super(PopUpDLG, self).exec_()
return self.retrunVal
class MainDialog(QtGui.QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
centralwidget = QtGui.QWidget(self)
self.layout = QtGui.QHBoxLayout(centralwidget)
self.button = QtGui.QPushButton("Open")
self.valText = QtGui.QLabel("")
self.layout.addWidget(self.button)
self.layout.addWidget(self.valText)
self.setCentralWidget(centralwidget)
self.button.clicked.connect(self.open_dialog)
def open_dialog(self):
dialog = PopUpDLG()
value = dialog.exec_()
if value:
self.valText.setText(value)
def main():
app = QtGui.QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
if __name__ == '__main__':
main()

Categories