I am trying to make dynamic widgets and make them clickable. By clicking on one widget it should pass dynamic value to other widget. I have tried sender() and other options accessing the widget but nothing worked.
All the widgets are sending information from the last widget.
Below is the code:
import sys
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QLabel, QMessageBox,QSizePolicy, QLayoutItem,QFrame,QHBoxLayout, QGridLayout, QVBoxLayout
from PyQt5.QtCore import QCoreApplication,QSize,QFileInfo
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from functools import partial
from PyQt5.QtCore import pyqtSlot
import sip
class Screen(QWidget):
def __init__(self):
super(Screen, self).__init__()
self.running_layout = QVBoxLayout()
self.action_layout = QVBoxLayout()
self.layout = QHBoxLayout(self)
self.layout.addLayout(self.running_layout)
self.layout.addLayout(self.action_layout)
self.setLayout(self.layout)
self.show()
self.all_running()
self.actions_1('1')
def buttonClicked(self):
sender = self.sender()
print(sender + ' was pressed')
def all_running(self):
for i in range(5):
button = QLabel("btn "+str(i))
button.mouseReleaseEvent = lambda _ : self.actions_1(str(button.text()).split()[1])
self.running_layout.addWidget(button)
button2 = QLabel("btn 5")
button2.mouseReleaseEvent = lambda _ : self.actions_1(str(button2.text()).split()[1])
self.running_layout.addWidget(button2)
def actions_1(self,value):
while not self.action_layout.isEmpty():
print(self.action_layout.isEmpty())
widget = self.action_layout.itemAt(0).widget()
print("removing", widget)
widget.setVisible(False)
self.action_layout.removeWidget(widget)
del widget
self.action_layout = QVBoxLayout()
self.layout.addLayout(self.action_layout)
val = int(value)
for i in range(val):
actions_item = QLabel(str(i))
self.action_layout.addWidget(actions_item)
app = QApplication(sys.argv)
Gui = Screen()
sys.exit(app.exec_())
Edited: I am trying to pass an int to actions based on the int value that number of widgets need to get populate.
This int is based on the dynamic widget which i generated using the for loop.
For example if the first widget of running_layout "btn 0" is clicked there won't be any widgets in the action_layout, If the second widget of running_layout "btn 1" is clicked there will be one widget "0" in the action_layout and so on based on the widget number.
I am able to send the signal but on whichever dynamically created widget i click it is sending value 4 of last widget "btn 4" to the action_layout.
To simplify the task we will create 2 widgets, the first one will be ClickableWidget that will have a signal indicating the number associated with the widget, whereas the second DynamicWidget will have a method that will be in charge of updating the number of widgets.
To know if an element is pressed we use the method installEventFilter() and eventFilter(), this filters the events giving information of the object and event, we will use setProperty("index", ) to store the number.
In the second widget I have implemented a method that is responsible for cleaning the widgets and creating some new widgets.
class ClickableWidget(QWidget):
clicked = pyqtSignal(int)
def __init__(self, n=5, parent=None):
QWidget.__init__(self, parent)
self.hlayout = QVBoxLayout(self)
for i in range(n):
label = QLabel("btn {}".format(i), self)
label.setProperty("index", i)
self.hlayout.addWidget(label)
label.installEventFilter(self)
def eventFilter(self, obj, event):
if isinstance(obj, QLabel) and event.type() == QEvent.MouseButtonPress:
i = obj.property("index")
self.clicked.emit(i)
return QWidget.eventFilter(self, obj, event)
class DynamicWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.hlayout = QVBoxLayout(self)
def changeWidget(self, n):
def clearLayout(layout):
item = layout.takeAt(0)
while item:
w = item.widget()
if w:
w.deleteLater()
lay = item.layout()
if lay:
clearLayout(item.layout())
item = layout.takeAt(0)
clearLayout(self.hlayout)
for i in range(n):
label = QLabel("btn {}".format(i), self)
self.hlayout.addWidget(label)
class Screen(QWidget):
def __init__(self):
super(Screen, self).__init__()
self.layout = QHBoxLayout(self)
c = ClickableWidget(6, self)
d = DynamicWidget(self)
c.clicked.connect(d.changeWidget)
self.layout.addWidget(c)
self.layout.addWidget(d)
Related
Here is my problem, I want to get the name, absolute position, and parent widget of an item from a QListView when drag and the QlistView widget where is been droped (and idealy the name or object of the item where it has been droped too).
For example with the code below : draging the item 'jaguar' on 'orange' should give me jaguar x=10 y=50 from widget "something" on orange x=30 y=70 from widget "somethingelse"
I tried to overide the function dropEvent of the QlistView but i get only the position of the QList viex not the item inside it.
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QApplication, QListView
from PyQt5.QtCore import Qt, QMimeData, QSize
from PyQt5.QtGui import QDrag, QIcon, QStandardItemModel, QStandardItem
class CustomListView(QListView):
def __init__(self, parent=None, **args):
super(CustomListView, self).__init__(parent, **args)
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.drag_item = None
self.drag_row = None
def dropEvent(self, event):
print("i was dropped")
pos = event.pos()
widget_dep = event.source()
print("event position",pos.x(),pos.y())
print("widget coordinate",widget_dep.x(),widget_dep.y())
### A custom widget containing my QlistView
class TableSelector(QWidget):
def __init__(self, parent, name, list_cols, checked=False):
super(TableSelector, self).__init__(parent)
self.name = name
self.liste_cols = list_cols
self.parent = parent
self.main_frame = QVBoxLayout()
self.model = QStandardItemModel()
self.listView = CustomListView(self)
# here is my problem i can never catch the event where i drag a row of the q list view
# if i apply the next line i can drag and drop it but make a copy of the object in the other QlistViex and in just want the positions of both of the object.
# self.listView.setDragDropMode(QAbstractItemView.DragDropMode)
for col in self.liste_cols:
item = QStandardItem(col)
item.setCheckable(True)
item.setEditable(False)
item.setDragEnabled(True)
item.setDropEnabled(True)
check = (Qt.Checked if checked else Qt.Unchecked)
item.setCheckState(check)
self.model.appendRow(item)
self.listView.setModel(self.model)
self.main_frame.addWidget(self.listView)
self.setLayout(self.main_frame)
class Window(QWidget):
def __init__(self):
super().__init__()
self.dic_table = {"animaux": ["jaguar", "chien", "chat", "phasme", "tortue", "raptor"],
"fruit": ["bananne", "pomme", "pamplemousse", "kiwi", "orange", "fraise", "framboise",
"poire", "groseille", "rhubarbe", "mangue", "litchee", "carambolle", "cerise",
"prune", "figue", "goyave", "tamarin"]}
self.mainLayout = QHBoxLayout()
for table in self.dic_table.keys() :
col_list = TableSelector(self, table, self.dic_table[table])
self.mainLayout.addWidget(col_list)
self.setLayout(self.mainLayout)
if __name__ == '__main__':
app = QApplication([])
w = Window()
w.show()
app.exec_()
In my app I have a QTabWidget which holds a variable number of seemingly "identical" tabs with a variable number of widgets.
I want, once the TAB (or shift-TAB) button is pressed, for the focus of the app to move to the next (or previous) tab, and focus on the corresponding widget of that tab (the one corresponding to the widget which had the focus until the key press).
What is the best way to go around this in a simple way? I tried using a QShortcut to catch the key-press but I can't seem to figure out a way to get the corresponding widget in the next or previous tab and focus on that.
Here's a minimal example of the code, which simply moves to the next tab but not to the corresponding widget:
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import *
class tabdemo(QTabWidget):
def __init__(self, num_tabs=2):
super().__init__()
shortcut = QtWidgets.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Tab), self)
shortcut.activated.connect(self.on_tab)
shortcut2 = QtWidgets.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Backtab), self)
shortcut2.activated.connect(self.on_shift_tab)
self.tabs = []
for i in range(num_tabs):
newtab = QWidget()
self.tabs.append(newtab)
self.addTab(newtab, f'Tab {i}')
self.add_widgets_to(newtab)
def add_widgets_to(self, tab):
layout = QVBoxLayout()
tab.setLayout(layout)
layout.addWidget(QSpinBox())
layout.addWidget(QCheckBox())
gender = QHBoxLayout()
gender.addWidget(QRadioButton("Male"))
gender.addWidget(QRadioButton("Female"))
layout.addLayout(gender)
#QtCore.pyqtSlot()
def on_tab(self):
current_tab = self.currentIndex()
self.setCurrentIndex((current_tab + 1) % self.count())
# TODO find current widget in focus, and find the corresponding one in the next tab, and focus on that one... note that widgets could be complex (i.e., not direct children...)
#QtCore.pyqtSlot()
def on_shift_tab(self):
print("do_something")
current_tab = self.currentIndex()
self.setCurrentIndex((current_tab - 1) % self.count())
def main():
app = QApplication(sys.argv)
ex = tabdemo()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Since the OP indicates that each page will have identical components then an index can be associated so that the index of the tab can be obtained before changing it and then set the widget's focus then set the focus to the other corresponding widget.
import sys
from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import (
QApplication,
QCheckBox,
QHBoxLayout,
QRadioButton,
QShortcut,
QSpinBox,
QTabWidget,
QVBoxLayout,
QWidget,
)
class Page(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
spinbox = QSpinBox()
checkbox = QCheckBox()
male_radio = QRadioButton("Male")
female_radio = QRadioButton("Female")
layout = QVBoxLayout(self)
layout.addWidget(spinbox)
layout.addWidget(checkbox)
gender = QHBoxLayout()
gender.addWidget(male_radio)
gender.addWidget(female_radio)
layout.addLayout(gender)
for i, widget in enumerate((spinbox, checkbox, male_radio, female_radio)):
widget.setProperty("tab_index", i)
class Tabdemo(QTabWidget):
def __init__(self, num_tabs=2):
super().__init__()
shortcut = QShortcut(QKeySequence(Qt.Key_Tab), self)
shortcut.activated.connect(self.next_tab)
shortcut2 = QShortcut(QKeySequence(Qt.Key_Backtab), self)
shortcut2.activated.connect(self.previous_tab)
for i in range(num_tabs):
page = Page()
self.addTab(page, f"Tab {i}")
#pyqtSlot()
def next_tab(self):
self.change_tab((self.currentIndex() + 1) % self.count())
#pyqtSlot()
def previous_tab(self):
self.change_tab((self.currentIndex() - 1) % self.count())
def change_tab(self, index):
focus_widget = QApplication.focusWidget()
tab_index = focus_widget.property("tab_index") if focus_widget else None
self.setCurrentIndex(index)
if tab_index is not None and self.currentWidget() is not None:
for widget in self.currentWidget().findChildren(QWidget):
i = widget.property("tab_index")
if i == tab_index:
widget.setFocus(True)
def main():
app = QApplication(sys.argv)
ex = Tabdemo()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Building on eyllanesc's answer, I improved the functionality to:
Account for the scrollbar location (if exists)
Use a bi-directional dictionary (implemented here) instead of a linear lookup
Dynamically add all relevant widgets using the update_map() method instead of having to add each widget manually.
Posting in case anyone finds this useful.
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import QWidget, QTabWidget, QShortcut, QApplication, QScrollArea
class BidirectionalDict(dict):
def __init__(self, *args, **kwargs):
super(BidirectionalDict, self).__init__(*args, **kwargs)
self.inverse = {}
for key, value in self.items():
self.inverse.setdefault(value, []).append(key)
def __setitem__(self, key, value):
if key in self:
self.inverse[self[key]].remove(key)
super(BidirectionalDict, self).__setitem__(key, value)
self.inverse.setdefault(value, []).append(key)
def __delitem__(self, key):
self.inverse.setdefault(self[key], []).remove(key)
if self[key] in self.inverse and not self.inverse[self[key]]:
del self.inverse[self[key]]
super(BidirectionalDict, self).__delitem__(key)
def get_first_inv(self, key):
return self.inverse.get(key, [None])[0]
class Page(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.widgets_map = BidirectionalDict()
# ... add your widgets ...
self.update_map()
def update_map(self):
widgets = self.findChildren(QWidget)
for i, widget in enumerate(widgets):
self.widgets_map[i] = widget
class MyQTabWidget(QTabWidget):
def __init__(self):
super().__init__()
shortcut = QShortcut(QKeySequence(Qt.Key_Tab), self)
shortcut.activated.connect(self.next_tab)
shortcut2 = QShortcut(QKeySequence(Qt.Key_Backtab), self)
shortcut2.activated.connect(self.previous_tab)
#pyqtSlot()
def next_tab(self):
self.change_tab((self.currentIndex() + 1) % self.count())
#pyqtSlot()
def previous_tab(self):
self.change_tab((self.currentIndex() - 1) % self.count())
def change_tab(self, new_tab_index):
old_tab: Page = self.currentWidget()
focus_widget = QApplication.focusWidget()
widget_index = old_tab.widgets_map.get_first_inv(focus_widget) if focus_widget else None
self.setCurrentIndex(new_tab_index)
new_tab: Page = self.currentWidget()
if new_tab is not None and widget_index is not None:
corresponding_widget: QWidget = new_tab.widgets_map[widget_index]
corresponding_widget.setFocus(True)
# Move scrollbar to the corresponding position
if hasattr(old_tab, 'scrollbar'):
# Tabs are identical so new_tab must have scrollbar as well
old_y = old_tab.scrollbar.verticalScrollBar().value()
scrollbar: QScrollArea = new_tab.scrollbar
scrollbar.verticalScrollBar().setValue(old_y)
I'm trying to add custom animation to QPushbutton without making a custom QPushbutton and overriding its enterEvent() and leaveEvent().
So far I've tried this,
#staticmethod
def addButtonHoverAnimation(button:QPushButton,currentPos:QPoint):
'''
Method to:
=> Add hover animation for provided button
'''
enterShift = QPropertyAnimation(button,b'pos',button)
exitShift = QPropertyAnimation(button,b'pos',button)
def enterEvent(e):
pos=button.pos()
enterShift.setStartValue(pos)
enterShift.setEndValue(QPoint(pos.x()+3,pos.y()+3))
enterShift.setDuration(100)
enterShift.start()
Effects.dropShadow(button,1,2)
def leaveEvent(e):
pos=button.pos()
exitShift.setStartValue(pos)
exitShift.setEndValue(QPoint(pos.x()-3,pos.y()-3))
exitShift.setDuration(100)
exitShift.start()
Effects.dropShadow(button)
button.enterEvent=enterEvent
button.leaveEvent=leaveEvent
But when I move the mouse very quickly in and out of the button before the animation finishes, The button starts to move wierdly towards the North-West direction.
Button Animation Using Dynamic Positions
I figured out this was due to the leaveEvent() being triggered before enterEvent() even finishes and also because the start and end values are dynamic. So, I tried providing currentPos as a static position and using it instead,
#staticmethod
def addButtonHoverAnimation(button:QPushButton,currentPos:QPoint):
'''
Method to:
=> Add hover animation for provided button
'''
enterShift = QPropertyAnimation(button,b'pos',button)
enterShift.setStartValue(currentPos)
enterShift.setEndValue(QPoint(currentPos.x()+3,currentPos.y()+3))
enterShift.setDuration(100)
exitShift = QPropertyAnimation(button,b'pos',button)
exitShift.setStartValue(QPoint(currentPos.x()-3,currentPos.y()-3))
exitShift.setEndValue(currentPos)
exitShift.setDuration(100)
def enterEvent(e):
button.setProperty(b'pos',exitShift.endValue())
enterShift.start()
Effects.dropShadow(button,1,2)
def leaveEvent(e):
exitShift.start()
Effects.dropShadow(button)
button.enterEvent=enterEvent
button.leaveEvent=leaveEvent
On running, as soon as the mouse enters the QPushbutton, it moves to the top-left of its parent widget and the animation starts working fine. I can't figure out why this is happening. But I was able to get that, it only happened when I used any static value in the animation.
Button Animation with Static Position:
Here is an example:
import sys
from PyQt5.QtCore import QEvent, QPoint, QObject, QPropertyAnimation
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
# This is the same method mentioned above
from styling import addButtonHoverAnimation
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout=QVBoxLayout()
button1 = QPushButton("Proceed1", self)
layout.addWidget(button1)
button2 = QPushButton("Proceed2", self)
layout.addWidget(button2)
self.setLayout(layout)
self.resize(640, 480)
addButtonHoverAnimation(button1)
addButtonHoverAnimation(button2)
def main():
app = QApplication(sys.argv)
view = Widget()
view.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
The problem is that probably when the state is changed from enter to leave (or vice versa) the previous animation still does not end so the position of the widget is not the initial or final position, so when starting the new animation there is a deviation that accumulates. One possible solution is to initialize the position and keep it as a reference.
On the other hand you should not do x.fooMethod = foo_callable since many can fail, in this case it is better to use an eventfilter.
import sys
from dataclasses import dataclass
from functools import cached_property
from PyQt5.QtCore import QEvent, QPoint, QObject, QPropertyAnimation
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget
#dataclass
class AnimationManager(QObject):
widget: QWidget
delta: QPoint = QPoint(3, 3)
duration: int = 100
def __post_init__(self):
super().__init__(self.widget)
self._start_value = QPoint()
self._end_value = QPoint()
self.widget.installEventFilter(self)
self.animation.setTargetObject(self.widget)
self.animation.setPropertyName(b"pos")
self.reset()
def reset(self):
self._start_value = self.widget.pos()
self._end_value = self._start_value + self.delta
self.animation.setDuration(self.duration)
#cached_property
def animation(self):
return QPropertyAnimation(self)
def eventFilter(self, obj, event):
if obj is self.widget:
if event.type() == QEvent.Enter:
self.start_enter_animation()
elif event.type() == QEvent.Leave:
self.start_leave_animation()
return super().eventFilter(obj, event)
def start_enter_animation(self):
self.animation.stop()
self.animation.setStartValue(self.widget.pos())
self.animation.setEndValue(self._end_value)
self.animation.start()
def start_leave_animation(self):
self.animation.stop()
self.animation.setStartValue(self.widget.pos())
self.animation.setEndValue(self._start_value)
self.animation.start()
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button1 = QPushButton("Proceed1", self)
button1.move(100, 100)
button2 = QPushButton("Proceed2", self)
button2.move(200, 200)
self.resize(640, 480)
animation_manager1 = AnimationManager(widget=button1)
animation_manager2 = AnimationManager(widget=button2)
def main():
app = QApplication(sys.argv)
view = Widget()
view.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
184 / 5000
Resultados de traducción
If you are using a layout then you must reset the position since the layout does not apply the position change immediately but only when the parent widget applies the changes.
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button1 = QPushButton("Proceed1")
button2 = QPushButton("Proceed2")
lay = QVBoxLayout(self)
lay.addWidget(button1)
lay.addWidget(button2)
self.resize(640, 480)
self.animation_manager1 = AnimationManager(widget=button1)
self.animation_manager2 = AnimationManager(widget=button2)
def resizeEvent(self, event):
super().resizeEvent(event)
self.animation_manager1.reset()
self.animation_manager2.reset()
Issue
When I drag to resize window in Qt, it is stretching the widgets inside my QVBoxLayout. I would like to avoid this.
Here are a few pictures of the demo app (created for this stackoverflow post):
Screenshot: More Widgets than Space (works as expected -> scoll down to view content)
Screenshots: Less Widgets than Space (problem - Qt is stretching my AccountWidget items)
How can I use Qt Layouts, or other Qt Widget classes to solve this problem? Here is the code for my Demo app (about 50 lines):
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, \
QGroupBox, QGridLayout, QLabel, QScrollArea
from typing import List
class AccountWidget(QWidget):
def __init__(self, data: dict, parent=None):
super().__init__(parent)
self.group_box_layout = QGridLayout()
for i, (key, value) in enumerate(data.items()):
self.group_box_layout.addWidget(QLabel(key), i+1, 1)
self.group_box_layout.addWidget(QLabel(value), i+1, 2)
self.group_box = QGroupBox()
self.group_box.setLayout(self.group_box_layout)
self.main_layout = QVBoxLayout()
self.main_layout.addWidget(self.group_box)
self.setLayout(self.main_layout)
class AccountListWidget(QWidget):
def __init__(self, data: List[dict], parent=None):
super().__init__(parent)
self.main_layout = QVBoxLayout()
for account_data in data:
self.main_layout.addWidget(AccountWidget(account_data))
self.setLayout(self.main_layout)
class MainWidget(QWidget):
def __init__(self, data: List[dict], parent=None):
super().__init__(parent)
self.account_list_widget = AccountListWidget(data)
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setWidget(self.account_list_widget)
self.main_layout = QVBoxLayout()
self.main_layout.addWidget(self.scroll_area)
self.setLayout(self.main_layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
data = [{"created":"2019","balance":"100","deposits":"50","type":"Chq"},
{"created":"2020","balance":"80","deposits":"45","type":"Chq"},
{"created":"2020","balance":"70","deposits":"55","type":"Sav"}]
mainWidget = MainWidget(data)
mainWindow = QMainWindow()
mainWindow.setCentralWidget(mainWidget)
mainWindow.setWindowTitle("Demo App")
mainWindow.resize(300, 300)
mainWindow.show()
app.exec()
Just add a stretch to the bottom of the layout (this is only possible for boxed layout):
class AccountListWidget(QWidget):
def __init__(self, data: List[dict], parent=None):
# ...
self.main_layout.addStretch()
If you need to do something like this on a grid layout, you can add a QSpacerItem or a single empty QWidget with its size policy set to expanded:
spacer = QtWidgets.QWidget()
spacer.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanded)
gridLayout.addWidget(spacer, gridLayout.rowCount(), 0)
Another solution is to set the size policy to the container (instead of adding the stretch):
class AccountListWidget(QWidget):
def __init__(self, data: List[dict], parent=None):
# ...
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
A couple of suggestions:
avoid using unnecessary container widgets: Your AccountWidget only contains a QGroupBox, so you should probably just subclass from QGroupBox instead;
grid layouts, as most things, use 0-based indexes, so your widgets should be added starting from row and column 0 (not 1); if you're doing that to add margins, use layout.setContentsMargins;
I am trying to create a pop-up window that gets popped-up from pressing on a QPushButton. However, I have a separate QPushButton class that I would like to use. I can't seem to get it working. Anything I am doing wrong?
#import ... statements
import sys
# from ... import ... statements
from PyQt5.QtWidgets import (QMainWindow, QApplication, QPushButton, QGridLayout, QWidget, QHBoxLayout, QLabel,
QVBoxLayout)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QFontDatabase, QColor, QPalette, QMovie
from skimage import transform, io
# Create main window of the widget
class MainWindow(QWidget):
def __init__(self):
super().__init__()
#Set a title inside the widget
self.titleLabel = QLabel()
titleText = "some title text"
self.titleLabel.setText(titleText)
# Give the label some flair
self.titleLabel.setFixedWidth(1000)
self.titleLabel.setAlignment(Qt.AlignCenter)
QFontDatabase.addApplicationFont(link_to_custom_font)
font = QFont()
font.setFamily("custom_font_name")
font.setPixelSize(50)
self.titleLabel.setFont(font)
# Set first button - The "Yes" Button
self.btn1 = myButtonOne("Yes")
#Initialize GUI
self.layoutGUI()
self.initUI()
def initUI(self):
self.fromleft = 200
self.fromtop = 100
self.w = 1000
self.h = 500
self.setGeometry(self.fromleft, self.fromtop, self.w, self.h)
def layoutGUI(self):
hbox = QHBoxLayout()
hbox.setSpacing(20)
hbox.addWidget(self.btn1)
vbox = QVBoxLayout()
vbox.addWidget(self.titleLabel)
vbox.addLayout(hbox)
self.setLayout(vbox)
class myButtonOne(QPushButton):
def __init__(self, parent=None):
super(myButtonOne, self).__init__(parent)
# Set maximum border size
imSize = io.imread(imagePath)
imHeight = imSize.shape[1]
imWidth = imSize.shape[0]
# Set first button - The "Yes" Button
yesImage = someImagePath
self.setStyleSheet("background-image: url(" + yesImage + ");"
"background-repeat: none;"
"background-position: center;"
"border: none")
self.setFixedSize(imWidth, imHeight)
self.clicked.connect(self.buttonOnePushed)
def buttonOnePushed(self):
textView().show()
def enterEvent(self, event):
newImage = someOtherImagePath
self.setStyleSheet("background-image: url("+newImage+");"
"background-repeat: none;"
"background-position: center;"
"border: none")
def leaveEvent(self, event):
newImage = someImagePath
self.setStyleSheet("background-image: url("+newImage+");"
"background-repeat: none;"
"background-position: center;"
"border: none")
class textView(QWidget):
def __init(self):
textView.__init__()
theText = QLabel()
#define sizes
self.height = 550
self.width = 250
# Open QWidget
self.initUI()
# Set the text for the QLabel
someText = "Some Text for the label"
theText.setText(someText)
def initUI(self):
self.show()
# Start GUI
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
So, I am trying to keep the QPushButton classes separate, so that I can customize them. I would like to keep it like that, especially to keep it clean and readable.
First off - please read: How to create a Minimal, Complete, and Verifiable example. I had a lot of work minimizing your code, which wasted a good amount of my time.
Nonetheless, here is a minimal working code, with your own button class:
import sys
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget, QLabel, QVBoxLayout
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.titleLabel = QLabel( "some label text" )
self.btn1 = myButtonOne( "button text" )
hbox = QVBoxLayout() # one vertical box seemed enough
hbox.addWidget( self.titleLabel )
hbox.addWidget( self.btn1 )
self.setLayout( hbox )
class myButtonOne(QPushButton):
def __init__(self, text, parent=None):
super(myButtonOne, self).__init__(text, parent)
self.clicked.connect(self.buttonOnePushed)
# add your customizations here
def buttonOnePushed (self) :
self.t = textView()
self.t.show()
class textView(QWidget):
def __init__(self):
super(textView, self).__init__()
self.theText = QLabel('test', self )
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
What have you done wrong in your code?
using textView().show() creates a local version of you textView-class and show()'s it:
def buttonOnePushed(self):
textView().show()
But, show() means that the code continues, where the end of your code comes, which results in cleaning up the locals. End - it's just shown for a microsecond.
def buttonOnePushed (self) :
self.t = textView()
self.t.show()
The code above stores the var as instance-attribute of the button, which is not cleaned up.
Furthermore you misspelled the init in your textView-class:
"__init" should be __init__ - else it is not called when using the constructor:
class textView(QWidget):
def __init(self):
textView.__init__()
Finally, you wanted to called show() twice:
in your textView-init you call initUI() which is calling show()
you calling show manually with textView().show()
Hope this helps! I did not include your personal style adjustments for readability.