How to display self defined QSplitter Class onto QMainWindow - python

I am currently trying to display a split screen view onto my window using PyQt5 with QSplitter and QMainWindow. I need QMainWindow to be able to create the menu options for the application that I am working on and would need to use QSplitter to use three separate windows for the application. Currently based on my code, the window is showing up, but there is no split screen shown at all. The only thing on the display is a blank screen when there should be the numbers: 1, 2, 3, and 4 shown in each corner.
I have tried to implement the QSplitters into the main window but then found that that approach does not work as it cannot set the layout of the QSplitters on top of setting the layout of the main window from QMainWindow. The next approach I used was the class based model where I defined what exactly I wanted for each splitter and implemented the subwindows into the main window and used two splitters (horizontal and vertical).
import sys
#imports
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QAction, QVBoxLayout, QStackedWidget
from PyQt5.QtWidgets import QMessageBox, QFrame, QSplitter, QTextEdit, QHBoxLayout, QLineEdit, QLabel
from PyQt5.QtGui import QKeySequence
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QIcon #to import icon
'''
class widget1(QWidget):
"""docstring for widget1"""
def __init__(self):
QWidget.__init__(self)
hbox = QHBoxLayout()
#create split screen
#this is the top left frame of the window
topleft = QFrame()
topleft.setFrameShape(QFrame.StyledPanel)
#first splitter
splitter1 = QSplitter(Qt.Horizontal)
lineedit = QLineEdit()
splitter1.addWidget(topleft)
splitter1.addWidget(lineedit)
splitter1.setSizes([200,200])
#second splitter and it is located at the bottom of the screen
bottom = QFrame()
bottom.setFrameShape(QFrame.StyledPanel)
splitter2 = QSplitter(Qt.Horizontal)
#add the splitter to the layout
hbox.addWidget(splitter2)
'''
class SubWindow(QWidget):
def __init__(self, label):
super(SubWindow, self).__init__()
self.label = QLabel(label)
self.label.setAlignment(Qt.AlignCenter)
self.label.setStyleSheet("QLabel {font-size:40px;}")
self.main_layout = QVBoxLayout()
self.main_layout.addWidget(self.label)
self.setLayout(self.main_layout)
#GUI class to create window
class GUI(QMainWindow):
"""docstring for GUI"""
def __init__(self,):
# Understand and explain ...
super().__init__()
#initialize the UI using the initUI method
self.initUI()
def initUI(self):
#set the title of the window
self.setWindowTitle('Visualization')
#Set the status bar in the beginning
self.statusBar().showMessage("In Progress")
#create new menubar object at top of window
menubar = self.menuBar()
#add file button to menubar
file_menu = menubar.addMenu("File")
#add edit button to menubar
edit_menu = menubar.addMenu("Edit")
#add view button to menubar
view_menu = menubar.addMenu("View")
#path for new icon and create the icon
new_icon = QIcon('newfileicon.png')
open_icon =QIcon('openfileicon.png')
exitapp_icon = QIcon('exitappicon.png')
#create new subbutton for the file menu and add icon and shortcuts
#as well as connecting the methods for each file menu
new_action = QAction(new_icon,'New', self)
new_action.setShortcut('Ctrl+N')
new_action.triggered.connect(self.newCall)
open_action = QAction(open_icon,'Open', self)
open_action.setShortcut('Ctrl+O')
open_action.triggered.connect(self.openCall)
exit_action = QAction(exitapp_icon,'Exit', self)
exit_action.setShortcut('Ctrl+X')
exit_action.triggered.connect(self.exitCall)
#add the action to the file menu button
file_menu.addAction(new_action)
file_menu.addAction(open_action)
file_menu.addAction(exit_action)
#when hovering over the "new", "open", and "exit", update statusbar
new_action.setStatusTip("Create New File")
open_action.setStatusTip("Open a file")
exit_action.setStatusTip("Exit application")
#when clicking the exit, close the application
exit_action.triggered.connect(self.close)
self.SubWindow1 = SubWindow("1")
self.SubWindow2 = SubWindow("2")
self.SubWindow3 = SubWindow("3")
self.SubWindow4 = SubWindow("4")
self.subsplitter1 = QSplitter(Qt.Horizontal)
self.subsplitter1.addWidget(self.SubWindow1)
self.subsplitter1.addWidget(self.SubWindow2)
self.subsplitter2 = QSplitter(Qt.Horizontal)
self.subsplitter2.addWidget(self.SubWindow3)
self.subsplitter2.addWidget(self.SubWindow4)
self.subsplitter = QSplitter(Qt.Vertical)
self.subsplitter.addWidget(self.subsplitter1)
self.subsplitter.addWidget(self.subsplitter2)
self.main_layout = QVBoxLayout()
self.main_layout.addWidget(self.subsplitter)
self.setLayout(self.main_layout)
#hbox = widget1()
#self.view = QHBoxLayout(self)
#resize the window to a 900x800 window
self.resize(900, 800)
def newCall(self):
QMessageBox.about(self, "Confirmation", "Are you sure you want to create a new file?")
def openCall(self):
QMessageBox.about(self, "Confirmation", "Are you sure you want to open a file?")
#if yes to the messagebox, then close, else dont close and pass
def exitCall(self):
reply = QMessageBox.question(self, "Confirmation", "Are you sure you want to exit the application?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
self.close
sys.exit()
else:
pass
#main function
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = GUI()
gui.show()
sys.exit(app.exec_())
I expected there to be the results of having four split screens with the values of "1", "2", "3", and "4" on each corner of the screen. Instead the output is what I have gotten before which is just a blank screen with the menubar and status bar being functional.

QMainWindow unlike QWidget already has a predefined layout:
So you should not use a layout to set the QSplitter but use the setCentralWidget() method of QMainWindow:
# ...
self.subsplitter = QSplitter(Qt.Vertical)
self.subsplitter.addWidget(self.subsplitter1)
self.subsplitter.addWidget(self.subsplitter2)
self.setCentralWidget(self.subsplitter)
self.resize(900, 800)
# ...

Related

How to show context menu at desired position in pyqt5?

My aim is to display context menu at desired position, base on mainwindow. Based on user mood, Mainwindow may be dragged from one position to another or shrink or grow. At this situation, context menu display position will change. If Mainwindow dragged to right most of screen, then the context menu will display left to qcombo box. if Mainwindow dragged to left most of the screen, then the context menu will display right to Qcombox position. If mainwindow is centre, then context menu will apper on right to qcombobox. simply context menu to display in available screen. (Not under the cursor or at a fixed postition)
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QComboBox, QPushButton, QVBoxLayout,QMenu
from PyQt5.QtCore import Qt,QPoint
class CheckableComboBox(QComboBox):
def __init__(self, xposi,yposi):
super().__init__()
self.xpo = xposi
self.ypo = yposi
self.setEditable(True)
self.lineEdit().setReadOnly(True)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.showMenu)
self.setStyleSheet("QComboBox QAbstractItemView::item {border: none;padding-left: 5px;}")
self.lineEdit().installEventFilter(self)
self.closeOnLineEditClick = True
self.double_click = None
def showMenu(self,pos):
menu = QMenu()
clear_action = menu.addAction("Clear Selection")
copy_action = menu.addAction(("Copy"))
action = menu.exec_(self.mapToGlobal(QPoint(self.xpo,self.ypo)))
if action == clear_action:
self.combo.setCurrentIndex(1)
class MyApp(QWidget):
def __init__(self):
super().__init__()
mainLayout = QVBoxLayout()
self.setLayout(mainLayout)
self.data = ['America', 'Russia', 'China', 'Germany', 'France', 'India']
self.combo = CheckableComboBox(100,100) #self.pos_x,self.pos_y)
self.combo.setFixedSize(600,60)
btn = QPushButton('Print Values')
btn.clicked.connect(self.getValue)
mainLayout.addWidget(self.combo)
mainLayout.addWidget(btn)
self.combo.addItems(self.data)
self.siz = None
def moveEvent(self,e):
self.siz = self.pos()
self.pos_x = self.siz.x()
self.pos_y = self.siz_y()
# super().moveEvent(e)
def getValue(self):
print(self.combo.currentData())
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyleSheet('''QWidget {font-size: 20px;}''')
myApp = MyApp()
myApp.show()
app.exit(app.exec_())
.

unable to re-open a window on pyqt5 pyqtlet

I have a basic GUI window, created with pyqt5 package, that contain a button that once clicked opens a map window thanks to Python's pyqtlet package.
My program does open the map window on first click, but here my issue:
If I close the map window and click again on the button it only show me a white window.
SOURCE CODE
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QGridLayout
from PyQt5.QtCore import Qt
from pyqtlet import L, MapWidget
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# set window title
self.setWindowTitle('Windowtitle')
# set layout
self.layout = QGridLayout()
self.layout.setAlignment(Qt.AlignCenter)
# create button to show the map
self.show_map_button = QPushButton('Show Map')
self.show_map_button.clicked.connect(self.show_map)
# add button to layout
self.layout.addWidget(self.show_map_button, 0, 1)
# show layout
self.setLayout(self.layout)
self.my_map = None
def show_map(self):
# create map window instance
self.my_map = MapWindow()
class MapWindow(QWidget):
def __init__(self):
# Setting up the widgets and layout
super().__init__()
self.mapWidget = MapWidget()
self.layout = QGridLayout()
self.layout.addWidget(self.mapWidget)
self.setLayout(self.layout)
# Working with the maps with pyqtlet
self.map = L.map(self.mapWidget)
self.map.setView([12.97, 77.59], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
self.marker = L.marker([12.934056, 77.610029])
self.marker.bindPopup('Maps are a treasure.')
self.map.addLayer(self.marker)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
I click the button for the first time (works as expected):
The second time I click it doesn't work as expected:
Please, can you help me find out where the issue is?
You are not trying to reopen the window but you are creating a new window but pyqtleft only allows you to register a "map" object. So if you want to reopen the window after the user closed it then check that if the object exists then just use the show method:
def show_map(self):
if self.my_map is None:
# create map window instance
self.my_map = MapWindow()
else:
self.my_map.show()

PyQt5 adding add and remove widget buttons beside every tab

I want to add buttons to the tabs in the QTabWidget.
My first instinct was to try to get the position of each tab and then add the button ontop of the tab, but I cant figure out how to get the position of the tab! Only the entire tab widget.
I was looking around and now what I think I should do is make a custom TabBar class where I can place buttons on each tab like the standard Qt close button.
Anyone here who can send me in the right direction?
Okay so I found out how to make it work like I want it. It was actually quite simple, I made a QWidget class with a horizontal layout and two buttons and passed it to the setTabButton function. For anyone interested see the code below.
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
class TabExample(QMainWindow):
def __init__(self):
super(TabExample, self).__init__()
self.setWindowTitle("Tab example")
# Create widgets
self.tab_widget = QtWidgets.QTabWidget()
self.setCentralWidget(self.tab_widget)
# Label's to fill widget
self.label1 = QtWidgets.QLabel("Tab 1")
self.label2 = QtWidgets.QLabel("Tab 2")
# Adding tab's
self.tab_widget.addTab(self.label1, "Tab 1")
self.tab_widget.addTab(self.label2, "Tab 2")
# Tab button's
self.right = self.tab_widget.tabBar().RightSide
self.tab_widget.tabBar().setTabButton(0, self.right, TabButtonWidget())
self.tab_widget.tabBar().setTabButton(1, self.right, TabButtonWidget())
# Tab settings
self.tab_widget.tabBar().setMovable(True)
self.show()
class TabButtonWidget(QtWidgets.QWidget):
def __init__(self):
super(TabButtonWidget, self).__init__()
# Create button's
self.button_add = QtWidgets.QPushButton("+")
self.button_remove = QtWidgets.QPushButton("-")
# Set button size
self.button_add.setFixedSize(16, 16)
self.button_remove.setFixedSize(16, 16)
# Create layout
self.layout = QtWidgets.QVBoxLayout()
self.layout.setSpacing(0)
self.layout.setContentsMargins(0, 0, 0, 0)
# Add button's to layout
self.layout.addWidget(self.button_add)
self.layout.addWidget(self.button_remove)
# Use layout in widget
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = TabExample()
sys.exit(app.exec_())

How to get data from dynamically created textboxes when a button is clicked in Pyqt5?

I am trying to make a window application in pyqt5 in which user enters a number then click("press me") button .
After that a number of rows are created according to the number entered by the user and one push button ("GO")
Each column has three labels with three textboxes
I managed to make the rows already , but what i can't manage is fetching the data from the textboxes when the push button is clicked
Note1: For simplicity , i was just trying the code for one textbox only then i will add more textboxes
Note2: i heard about some function called Lambda , but i searched for it and i couldn't find good explanation to it
Note3: Similar questions that didn't work for me:
Acessing dynamically added widgets I didn't know how to use this answer as i have two kinds of widgets in the layout , label and qlinedit
getting values from dynamically created qlinedits This answer didn't suit my case as i want one only button to get the data in all created textboxes
Code :
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5 import *
from PyQt5.QtWidgets import QLineEdit,QLabel,QGridLayout
import sys
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.home()
def home(self):
self.grid=QGridLayout()
self.setLayout(self.grid)
self.label=QLabel(self)
self.label.setText("NO")
self.grid.addWidget(self.label,0,1)
self.pushButton_ok = QtWidgets.QPushButton("Press me", self)
self.pushButton_ok.clicked.connect(self.addtextbox)
self.grid.addWidget(self.pushButton_ok,0,10)
self.input1=QLineEdit(self)
self.grid.addWidget(self.input1,0,5)
def addtextbox(self):
no_of_process=(self.input1.text())
no=int(no_of_process)
n=0
while(n<no):
self.bursttime=QLabel(self)
self.bursttime.setText("b")
self.timeinput=QLineEdit(self)
self.grid.addWidget(self.bursttime,2*n+1,0)
self.grid.addWidget(self.timeinput,2*n+1,1)
n=n+1
self.go=QtWidgets.QPushButton("GO",self)
self.grid.addWidget(self.go,6,0)
self.go.clicked.connect(self.printvalues)
def printvalues():
n=0
#fetch data in some way
application = QtWidgets.QApplication(sys.argv)
window = Window()
window.setWindowTitle('Dynamically adding textboxes using a push button')
window.resize(250, 180)
window.show()
sys.exit(application.exec_())
Main Window of program
when user enters for example 2 to create 2 rows
Try it:
import sys
from PyQt5.QtWidgets import (QLineEdit, QLabel, QGridLayout, QWidget,
QPushButton, QApplication, QSpinBox)
class Window(QWidget):
def __init__(self):
super().__init__()
self.home()
def home(self):
self.grid = QGridLayout()
self.setLayout(self.grid)
self.label = QLabel(self)
self.label.setText("NO")
self.grid.addWidget(self.label, 0, 1)
# self.input1 = QLineEdit(self)
self.input1 = QSpinBox(self) # +++
self.input1.setMinimum(1)
self.input1.setMaximum(12)
self.input1.setValue(3)
self.grid.addWidget(self.input1, 0, 5)
self.pushButton_ok = QPushButton("Press me", self)
self.pushButton_ok.clicked.connect(self.addtextbox) #(self.addCheckbox)
self.grid.addWidget(self.pushButton_ok, 0, 10)
def addtextbox(self):
countLayout = self.layout().count()
if countLayout > 3:
for it in range(countLayout - 3):
w = self.layout().itemAt(3).widget()
self.layout().removeWidget(w)
w.hide()
self.lineEdits = [] # +++
for n in range(self.input1.value()):
self.bursttime = QLabel(self)
self.bursttime.setText("b_{}".format(n))
self.timeinput = QLineEdit(self)
self.timeinput.textChanged.connect(lambda text, i=n : self.editChanged(text, i)) # +++
self.grid.addWidget(self.bursttime, 2*n+1, 0)
self.grid.addWidget(self.timeinput, 2*n+1, 1)
self.lineEdits.append('') # +++
self.go = QPushButton("GO") #, self)
self.grid.addWidget(self.go, 2*n+2, 0)
self.go.clicked.connect(self.printvalues)
def printvalues(self):
# fetch data in some way
for i, v in enumerate(self.lineEdits): # +++
print("bursttime: b_{}, timeinput: {}".format(i, v)) # +++
def editChanged(self, text, i): # +++
self.lineEdits[i] = text # +++
def addCheckbox(self):
print("def addCheckbox(self):")
if __name__ == "__main__":
application = QApplication(sys.argv)
window = Window()
window.setWindowTitle('Dynamically adding textboxes using a push button')
window.resize(250, 180)
window.show()
sys.exit(application.exec_())
I was working on a PyQt5 app which had a dynamically loaded whole tab, with QTableView, QLineEdit and few QPushButtons, and had similar problem, I needed data from that one QLineEdit every tab. I've used QSignalMapper because I had to take data on textChanged() signal, but since you have a simple push button to just grab data, you can use QObject.findChildren() like I did in this example:
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5 import *
from PyQt5.QtWidgets import QLineEdit,QLabel,QGridLayout
import sys
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.home()
def home(self):
self.grid=QGridLayout()
self.setLayout(self.grid)
self.label=QLabel(self)
self.label.setText("NO")
self.grid.addWidget(self.label,0,1)
self.pushButton_ok = QtWidgets.QPushButton("Press me", self)
self.pushButton_ok.clicked.connect(self.addtextbox)
self.grid.addWidget(self.pushButton_ok,0,10)
self.input1=QLineEdit(self)
self.grid.addWidget(self.input1,0,5)
def addtextbox(self):
no_of_process=(self.input1.text())
no=int(no_of_process)
n=0
while(n<no):
self.bursttime=QLabel(self)
self.bursttime.setText("b")
self.timeinput=QLineEdit(self)
self.timeinput.setObjectName("timeinput_{0}".format(n))
self.grid.addWidget(self.bursttime,2*n+1,0)
self.grid.addWidget(self.timeinput,2*n+1,1)
n=n+1
self.go=QtWidgets.QPushButton("GO",self)
self.grid.addWidget(self.go,6,0)
self.go.clicked.connect(self.printvalues)
def printvalues(self):
for child in self.findChildren(QLineEdit, QtCore.QRegExp("timeinput_(\d)+")):
print(child.text())
application = QtWidgets.QApplication(sys.argv)
window = Window()
window.setWindowTitle('Dynamically adding textboxes using a push button')
window.resize(250, 180)
window.show()
sys.exit(application.exec_())
P.S. I fixed your pushButton_ok.clicked() signal, it was calling addCheckBox() which doesn't exist.

How to customize QPushButton to only popup menu when clicked around the arrow?

I want to add a popup menu to QPushButton, but only popup it when you click near the arrow, if you click other area on the button, it calls the slot connected in main UI.
I know there is QToolButton, and you can set its ToolButtonPopupMode to MenuButtonPopup, but for some reason it looks different than then rest of the button on my UI, I assume I could somehow modify the style of it to make it look exactly like QPushButton, anyway in the end I decided to subclass QPushButton instead.
The problems in the following code are:
1. How do I get the rect of the arrow, maybe show a dashed rect around the arrow, I thought the "popup menu hotspot" area should be a little bit bigger than the arrow. right now I hardcoded 20px, but I think it should be retrieved from QStyle?
[solved] How to make the button look "pressed" when clicked not near the arrow, right now its look does not change, I guess it's because I did not call base class MousePressEvent, because I don't want the menu to popup when clicked elsewhere.
How to move the position of the arrow, in my applicaton it is too close to the right edge, how can I move it to the left a little bit?
code:
from PyQt4 import QtGui, QtCore
import sys
class MyButton(QtGui.QPushButton):
def __init__(self, parent=None):
super(MyButton, self).__init__(parent)
def mousePressEvent(self, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
# figure out press location
pos = event.pos
topRight = self.rect().topRight()
bottomRight = self.rect().bottomRight()
frameWidth = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
print topRight, bottomRight, frameWidth
# get the rect from QStyle instead of hardcode numbers here
arrowTopLeft = QtCore.QPoint(topRight.x()-20, topRight.y())
arrowRect = QtCore.QRect(arrowTopLeft, bottomRight)
if arrowRect.contains(event.pos()):
print 'clicked near arrow'
# event.accept()
QtGui.QPushButton.mousePressEvent(self, event)
else:
print 'clicked outside'
# call the slot connected, without popup the menu
# the following code now does not make
# the button pressed
self.clicked.emit(True)
event.accept()
class Main(QtGui.QDialog):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QtGui.QVBoxLayout()
pushbutton = MyButton('Popup Button')
layout.addWidget(pushbutton)
menu = QtGui.QMenu()
menu.addAction('This is Action 1', self.Action1)
menu.addAction('This is Action 2', self.Action2)
pushbutton.setMenu(menu)
self.setLayout(layout)
pushbutton.clicked.connect(self.button_press)
def button_press(self):
print 'You pressed button'
def Action1(self):
print 'You selected Action 1'
def Action2(self):
print 'You selected Action 2'
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Main()
main.show()
app.exec_()
edit:
it seems this will stop the menu from poping up if clicked on the left side of the button
else:
print 'clicked outside'
self.blockSignals(True)
QtGui.QPushButton.mousePressEvent(self, event)
self.blockSignals(False)
Have you thought on using a QComboBox?
Or maybe two buttons next to each other one for appearance only, and the other that calls your context?
Would work to use mask on your button through pixmap.
You also could make some use of setStyleSheet("") can make some use of these attributes.
Here is a little example:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QScrollArea
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget
class WPopUpButton(QWidget):
"""WPopUpButton is a personalized QPushButton."""
w_container = None
v_layout_container = None
v_scroll_area = None
v_layout_preview = None
def __init__(self):
"""Init UI."""
super(WPopUpButton, self).__init__()
self.init_ui()
def init_ui(self):
"""Init all ui object requirements."""
self.button_that_do_nothing = QPushButton("Popup Button")
self.button_that_do_nothing.setStyleSheet("""
border: 0px;
background: gray;
""")
self.button_that_do_something = QPushButton("->")
#you can also set icon, to make it look better :D
self.button_that_do_something.setStyleSheet("""
border: 0px;
background: gray;
""")
self.layout = QHBoxLayout()
self.layout.setSpacing(0)
self.layout.setContentsMargins(0,0,0,0)
self.layout.addWidget(self.button_that_do_nothing)
self.layout.addWidget(self.button_that_do_something)
self.setLayout(self.layout)
self.create_connections()
def create_connections(self):
self.button_that_do_something.pressed.connect(self.btn_smtg_pressed)
self.button_that_do_something.released.connect(self.btn_smtg_released)
def btn_smtg_pressed(self):
self.button_that_do_something.setStyleSheet("""
border: 0px;
background: blue;
""")
def btn_smtg_released(self):
self.button_that_do_something.setStyleSheet("""
border: 0px;
background: gray;
""")
# HERE YOU DO WHAT YOU NEED
# FOR EXAMPLE CALL YOUR CONTEXT WHATEVER :D
def run():
app = QApplication(sys.argv)
GUI = WPopUpButton()
GUI.show()
sys.exit(app.exec_())
run()
By the way I'm using Pyqt5, you just gotta change your imports ")
Here's another option that may partially answer your question.
Instead of using the default menu, you can combine CustomContextMenu and custom arrow created by either QLabel and/or .png images.
setContentsMargins in the code will allow a much more flexible layout.
sample image
import os
from PyQt5.QtWidgets import (
QDialog,
QPushButton,
QApplication,
QVBoxLayout,
QMenu,
QStyle,
QHBoxLayout,
QLabel,
)
from PyQt5.QtCore import (
QEvent,
QPoint,
QRect,
Qt,
QSize,
)
from PyQt5.QtGui import (
QIcon,
QMouseEvent,
)
import sys
import functools
import copy
class MyButton(QPushButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.clicked_near_arrow = None
# set icon by letter
self.label_icon = QLabel(" ▼ ")
self.label_icon.setAttribute(Qt.WA_TranslucentBackground)
self.label_icon.setAttribute(Qt.WA_TransparentForMouseEvents)
icon_size = QSize(19, 19)
# set icon by picture
self.pixmap_default = QIcon("default_button.png").pixmap(icon_size) # prepare images if necessary
self.pixmap_presssed = QIcon("pressed_button.png").pixmap(icon_size) # prepare images if necessary
self.pic_icon = QLabel()
self.pic_icon.setAttribute(Qt.WA_TranslucentBackground)
self.pic_icon.setAttribute(Qt.WA_TransparentForMouseEvents)
self.pic_icon.setPixmap(self.pixmap_default)
# layout
lay = QHBoxLayout(self)
lay.setContentsMargins(0, 0, 6, 3)
lay.setSpacing(0)
lay.addStretch(1)
lay.addWidget(self.pic_icon)
lay.addWidget(self.label_icon)
def set_icon(self, pressed):
if pressed:
self.label_icon.setStyleSheet("QLabel{color:white}")
self.pic_icon.setPixmap(self.pixmap_presssed)
else:
self.label_icon.setStyleSheet("QLabel{color:black}")
self.pic_icon.setPixmap(self.pixmap_default)
def mousePressEvent(self, event):
if event.type() == QEvent.MouseButtonPress:
self.set_icon(pressed=True)
# figure out press location
topRight = self.rect().topRight()
bottomRight = self.rect().bottomRight()
# get the rect from QStyle instead of hardcode numbers here
arrowTopLeft = QPoint(topRight.x()-19, topRight.y())
arrowRect = QRect(arrowTopLeft, bottomRight)
if arrowRect.contains(event.pos()):
self.clicked_near_arrow = True
self.blockSignals(True)
QPushButton.mousePressEvent(self, event)
self.blockSignals(False)
print('clicked near arrow')
self.open_context_menu()
else:
self.clicked_near_arrow = False
QPushButton.mousePressEvent(self, event)
def mouseMoveEvent(self, event):
if self.rect().contains(event.pos()):
self.set_icon(pressed=True)
else:
self.set_icon(pressed=False)
QPushButton.mouseMoveEvent(self, event)
def mouseReleaseEvent(self, event):
self.set_icon(pressed=False)
if self.clicked_near_arrow:
self.blockSignals(True)
QPushButton.mouseReleaseEvent(self, event)
self.blockSignals(False)
else:
QPushButton.mouseReleaseEvent(self, event)
def setMenu(self, menu):
self.menu = menu
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.open_context_menu)
# ContextMenueのlauncher
def open_context_menu(self, point=None):
point = QPoint(7, 23)
self.menu.exec_(self.mapToGlobal(point))
event = QMouseEvent(QEvent.MouseButtonRelease, QPoint(10, 10), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
self.mouseReleaseEvent(event)
class Main(QDialog):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
menu = QMenu()
menu.addAction('This is Action 1', self.Action1)
menu.addAction('This is Action 2', self.Action2)
pushbutton = MyButton('Popup Button')
pushbutton.setMenu(menu)
layout = QVBoxLayout()
layout.addWidget(pushbutton)
self.setLayout(layout)
# event connect
pushbutton.setAutoDefault(False)
pushbutton.clicked.connect(self.button_press)
def button_press(self):
print('You pressed button')
def Action1(self):
print('You selected Action 1')
def Action2(self):
print('You selected Action 2')
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Main()
main.show()
app.exec_()

Categories