Python Forum

Full Version: right mouse button click with PyQt5
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I'm trying to write a mineweeper program in PyQt5
This is what I already have.
I still have 2 problems:
1* How do I detect a right mouse button click on a button? As you can see I already have a left mouse button click
2* I created a grid of buttons but I'm unable to remove the space between the buttons. How do I do that

I also don't understand the differece between
class Window(QDialog)
and
class Window(QtWidgets.QMainWindow):
import sys
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from functools import partial
from random import randint

class Window(QDialog):
	def __init__(self):
		super().__init__()
		self.setGeometry(50,50,600,300)
		self.setWindowTitle("Minesweeper")
		self.setWindowIcon(QtGui.QIcon('favicon.png'))
		
		self.initGame()		
		
		self.mainPage()
	
	def mainPage(self):
		
		self.createGridLayout()

		windowLayout = QVBoxLayout()
		windowLayout.addWidget(self.horizontalGroupBox)
		self.setLayout(windowLayout)
		
		self.show()
	
	def initGame(self):
		self.initialArray=[[0 for i in range(10)]for j in range(10)]
		self.counterTurns=0
		
		for column in range (0,10):
			for row in range (0,10):
				bomb=randint(0,6)
				if bomb==6:
					self.initialArray[row][column]=9
					if row-1>=0 and self.initialArray[row-1][column]!=9:
						self.initialArray[row-1][column]+=1
					if row+1<=9 and self.initialArray[row+1][column]!=9:
						self.initialArray[row+1][column]+=1
					if column-1>=0 and self.initialArray[row][column-1]!=9:
						self.initialArray[row][column-1]+=1
					if column+1<=9 and self.initialArray[row][column+1]!=9:
						self.initialArray[row][column+1]+=1
					if row-1>=0 and column-1>=0 and self.initialArray[row-1][column-1]!=9:
						self.initialArray[row-1][column-1]+=1
					if row+1<=9 and column-1>=0 and self.initialArray[row+1][column-1]!=9:
						self.initialArray[row+1][column-1]+=1
					if row-1>=0 and column+1<=9 and self.initialArray[row-1][column+1]!=9:
						self.initialArray[row-1][column+1]+=1
					if row+1<=9 and column+1<=9 and self.initialArray[row+1][column+1]!=9:
						self.initialArray[row+1][column+1]+=1
	
		

	def createGridLayout(self):
		self.horizontalGroupBox=QGroupBox()
		layout=QGridLayout()
		
		self.button={}
		
		for column in range (0,10):
			for row in range (0,10):
				self.button[row,column]=QPushButton(self)
				self.button[row,column].setFixedHeight(20)
				self.button[row,column].setFixedWidth(20)
				self.button[row,column].setIcon(QtGui.QIcon('tile_plain.gif'))
				self.button[row,column].clicked.connect(partial(self.buttonPressed,row,column))
				layout.addWidget(self.button[row,column],row,column)
				

		for row in self.initialArray:
			print (row)
		self.horizontalGroupBox.setLayout(layout)
		
	def buttonPressed(self,row,column):
		self.displayEmptyButtons(row,column)
		self.counterTurns+=1
		print ('rij'+str(row)+'\nkolom'+str(column))
		if self.initialArray[row][column]==9:
			print ('bomb!!!! ' + str(self.counterTurns))
		game_busy=0
		values=[1,2,3,4,5,6,7,8]
		for x in self.initialArray:
			print (x)
			if any(i in values for i in x):
				game_busy=1
		if game_busy==0:
			print ('you won')
			
		
				
	def displayEmptyButtons(self,row,column):
		if self.initialArray[row][column]!=0:
			if self.initialArray[row][column]!=10:
				self.button[row,column].setText(str(self.initialArray[row][column]))
			self.initialArray[row][column]=10
		else:
			if self.initialArray[row][column]!=10:
				self.button[row,column].setText(str(self.initialArray[row][column]))
			self.initialArray[row][column]=10
			if row-1>=0:
				self.displayEmptyButtons(row-1,column)
			if row+1<=9:
				self.displayEmptyButtons(row+1,column)
			if column-1>=0:
				self.displayEmptyButtons(row,column-1)
			if column+1<=9:
				self.displayEmptyButtons(row,column+1)
			if row-1>=0 and column-1>=0:
				self.displayEmptyButtons(row-1,column-1)
			if row+1<=9 and column-1>=0:
				self.displayEmptyButtons(row+1,column-1)
			if row-1>=0 and column+1<=9:
				self.displayEmptyButtons(row-1,column+1)
			if row+1<=9 and column+1<=9:
				self.displayEmptyButtons(row+1,column+1)

			
if __name__ == '__main__':
    
    app = QApplication(sys.argv)
    ex = Window()
    sys.exit(app.exec_())
To remove the space between buttons, you must apply these settings on the layout (or edit the Layout section in QtDesigner):
        self.gridLayout.setSpacing(0)
        self.gridLayout.setContentsMargins(0,0,0,0)
Then to detect the type of mouse click, or even the mouse wheel, you must install an event filter on each button:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(QtWidgets.QMainWindow):
    def setupUi(self, Form):
        self.gridLayout = QtWidgets.QGridLayout(Form)
        self.gridLayout.setSpacing(0)
        self.gridLayout.setContentsMargins(0,0,0,0)
        self.gridLayout.setObjectName("gridLayout")

        self.pushButton_1 = QtWidgets.QPushButton(Form)
        self.pushButton_1.setFixedSize(QtCore.QSize(20, 20))
        self.pushButton_1.setObjectName("pushButton_1")
        self.gridLayout.addWidget(self.pushButton_1, 0, 0, 1, 1)

        self.pushButton_2 = QtWidgets.QPushButton(Form)
        self.pushButton_2.setFixedSize(QtCore.QSize(20, 20))
        self.pushButton_2.setObjectName("pushButton_2")
        self.gridLayout.addWidget(self.pushButton_2, 1, 0, 1, 1)

        self.pushButton_3 = QtWidgets.QPushButton(Form)
        self.pushButton_3.setFixedSize(QtCore.QSize(20, 20))
        self.pushButton_3.setObjectName("pushButton_3")
        self.gridLayout.addWidget(self.pushButton_3, 0, 1, 1, 1)

        self.pushButton_4 = QtWidgets.QPushButton(Form)
        self.pushButton_4.setFixedSize(QtCore.QSize(20, 20))
        self.pushButton_4.setObjectName("pushButton_4")
        self.gridLayout.addWidget(self.pushButton_4, 1, 1, 1, 1)

        self.pushButton_1.installEventFilter(self)
        self.pushButton_2.installEventFilter(self)
        self.pushButton_3.installEventFilter(self)
        self.pushButton_4.installEventFilter(self)

    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            if event.button() == QtCore.Qt.LeftButton:
                print(obj.objectName(), "Left click")
            elif event.button() == QtCore.Qt.RightButton:
                print(obj.objectName(), "Right click")
            elif event.button() == QtCore.Qt.MiddleButton:
                print(obj.objectName(), "Middle click")
        return QtCore.QObject.event(obj, event)


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())
Finally, the choice between a Dialog and a MainWindow depend of what you intend to do with it. The dialog usually have some action button (apply, ok, cancel...), and therefore they have accepted and rejected signals. The MainWindow have more complex features, such as a top menu and status bar. It is very well detailled in Qt docs:

https://doc.qt.io/qt-5/qmainwindow.html
https://doc.qt.io/archives/qt-4.8/qdialog.html
There is still something going wrong with the mouse click. Any idea what I'm doing wrong?
This is the error message I'm getting:

Traceback (most recent call last):
File "minesweeper_A01.py", line 53, in eventFilter
if event.button() == Qt.LeftButton:
AttributeError: 'QEvent' object has no attribute 'button'


------------------
(program exited with code: 3)

Druk op een toets om door te gaan. . .


import sys
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from functools import partial
from random import randint

#class Window(QWidget):
class Window(QMainWindow):
	def __init__(self):
		super(Window,self).__init__()
		self.setWindowTitle("Minesweeper")
		self.setWindowIcon(QtGui.QIcon('ekopak-favicon.png'))
		
		fileAction = QAction("New", self)
		fileAction.triggered.connect(self.init_game)

		mainMenu = self.menuBar()
		fileMenu = mainMenu.addMenu('&File')
		fileMenu.addAction(fileAction)
		
		self.setMouseTracking(True)
		
		self.init_game()		
		
		self.main_page()
	
	def main_page(self):		
		self.button={}
		
		self.centralwidget = QWidget()
		self.setCentralWidget(self.centralwidget)
        
		horizontalLayout=QHBoxLayout(self.centralwidget)
		for column in range (0,10):
			verticalLayout=QVBoxLayout()
			verticalLayout.setSpacing(0)
			for row in range (0,10):
				self.button[row,column]=QPushButton(self)
				self.button[row,column].setFixedHeight(20)
				self.button[row,column].setFixedWidth(20)
				self.button[row,column].setContentsMargins(0,0,0,0)
				self.button[row,column].setIcon(QtGui.QIcon('tile_plain.gif'))
				self.button[row,column].installEventFilter(self)
				self.button[row,column].clicked.connect(partial(self.button_pressed,row,column))
				verticalLayout.addWidget(self.button[row,column])
			horizontalLayout.addLayout(verticalLayout)
			horizontalLayout.setSpacing(0)
		self.show()
	
	def eventFilter(self,obj,event):
		print('button pressed')
		if event.button() == Qt.LeftButton:
			print(obj.objectName(), "Left click")
		elif event.button() == Qt.RightButton:
			print(obj.objectName(), "Right click")
		elif event.button() == Qt.MiddleButton:
			print(obj.objectName(), "Middle click")
		return QObject.event(obj, event)


		

	
	def init_game(self):
		self.initialArray=[[0 for i in range(10)]for j in range(10)]
		self.counterTurns=0
		
		for column in range (0,10):
			for row in range (0,10):
				bomb=randint(0,6)
				if bomb==6:
					self.initialArray[row][column]=9
					if row-1>=0 and self.initialArray[row-1][column]!=9:
						self.initialArray[row-1][column]+=1
					if row+1<=9 and self.initialArray[row+1][column]!=9:
						self.initialArray[row+1][column]+=1
					if column-1>=0 and self.initialArray[row][column-1]!=9:
						self.initialArray[row][column-1]+=1
					if column+1<=9 and self.initialArray[row][column+1]!=9:
						self.initialArray[row][column+1]+=1
					if row-1>=0 and column-1>=0 and self.initialArray[row-1][column-1]!=9:
						self.initialArray[row-1][column-1]+=1
					if row+1<=9 and column-1>=0 and self.initialArray[row+1][column-1]!=9:
						self.initialArray[row+1][column-1]+=1
					if row-1>=0 and column+1<=9 and self.initialArray[row-1][column+1]!=9:
						self.initialArray[row-1][column+1]+=1
					if row+1<=9 and column+1<=9 and self.initialArray[row+1][column+1]!=9:
						self.initialArray[row+1][column+1]+=1	
		for row in self.initialArray:
			print (row)

		
	def button_pressed(self,row,column):
		self.display_empty_buttons(row,column)
		self.counterTurns+=1
		print ('rij'+str(row)+'\nkolom'+str(column))
		if self.initialArray[row][column]==9:
			print ('bomb!!!! ' + str(self.counterTurns))
		game_busy=0
		values=[1,2,3,4,5,6,7,8]
		for x in self.initialArray:
			print (x)
			if any(i in values for i in x):
				game_busy=1
		if game_busy==0:
			print ('you won')
			
		
				
	def display_empty_buttons(self,row,column):
		#the value of initialArray can be 0-8, 9 is for a bomb, 10 is if it has been opened
		if self.initialArray[row][column]!=0:
			if self.initialArray[row][column]!=10:
				if self.initialArray[row][column]==0:
					self.button[row,column].setIcon(QtGui.QIcon('tile_clicked.gif'))
				elif self.initialArray[row][column]==1:
					self.button[row,column].setIcon(QtGui.QIcon('tile_1.gif'))	
				elif self.initialArray[row][column]==2:
					self.button[row,column].setIcon(QtGui.QIcon('tile_2.gif'))
				elif self.initialArray[row][column]==3:
					self.button[row,column].setIcon(QtGui.QIcon('tile_3.gif'))	
				elif self.initialArray[row][column]==4:
					self.button[row,column].setIcon(QtGui.QIcon('tile_4.gif'))	
				elif self.initialArray[row][column]==5:
					self.button[row,column].setIcon(QtGui.QIcon('tile_5.gif'))
				elif self.initialArray[row][column]==6:
					self.button[row,column].setIcon(QtGui.QIcon('tile_6.gif'))	
				elif self.initialArray[row][column]==7:
					self.button[row,column].setIcon(QtGui.QIcon('tile_7.gif')) 
				elif self.initialArray[row][column]==8:
					self.button[row,column].setIcon(QtGui.QIcon('tile_8.gif'))
				elif self.initialArray[row][column]==9:
					self.button[row,column].setIcon(QtGui.QIcon('tile_mine.gif'))
				else:
					self.button[row,column].setText(str(self.initialArray[row][column]))
			self.initialArray[row][column]=10
		else:
			if self.initialArray[row][column]!=10:
				if self.initialArray[row][column]==0:
					self.button[row,column].setIcon(QtGui.QIcon('tile_clicked.gif'))
				else:
					self.button[row,column].setText(str(self.initialArray[row][column]))
			self.initialArray[row][column]=10
			if row-1>=0:
				self.displayEmptyButtons(row-1,column)
			if row+1<=9:
				self.displayEmptyButtons(row+1,column)
			if column-1>=0:
				self.displayEmptyButtons(row,column-1)
			if column+1<=9:
				self.displayEmptyButtons(row,column+1)
			if row-1>=0 and column-1>=0:
				self.displayEmptyButtons(row-1,column-1)
			if row+1<=9 and column-1>=0:
				self.displayEmptyButtons(row+1,column-1)
			if row-1>=0 and column+1<=9:
				self.displayEmptyButtons(row-1,column+1)
			if row+1<=9 and column+1<=9:
				self.displayEmptyButtons(row+1,column+1)

#messagebox bomb pressed
#messagebox won
#rightclick add flag
					
			
if __name__ == '__main__':
    
    app = QApplication(sys.argv)
    ex = Window()
    sys.exit(app.exec_())
It is because not all event have the same properties. Therefore you must verify what type of event it is before looking for the button attribute.

if event.type() == QtCore.QEvent.MouseButtonPress:
Thanks for the help, it's working now