How do I access outer class variables in inner class(PyQt)? - python

It is not a duplicate! I didn't want to make title really long, so it's short version may seem as a duplicate. The problem is a little bit bigger than in the title.
Environment: I'm trying to build painting app using PyQt5 and Qt-Designer. I've got three files in my project: main.py, slider.py, ui.py(UI from Qt-Designer generated by pyuic).
What do I need: I need to change App's brush size(self.variable) every time Slider value changes.
The problem itself: App Class inherits Ui_MainWindow Class from the file that I can't edit(it is generated every time). Ui_MainWindow sets Slider Class as its attribute. So basically Slider is App's attribute, and I need to change App's variable "canvas" while being in Slider Class.
I don't know how to access outer class variable in inner class method. Especially when I can't edit ui.py, so I could call Slider(self) instead of Slider(self.sidebar).
main.py
import sys
from PyQt5 import uic
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from ui import Ui_MainWindow
class App(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
if __name__ == '__main__':
application = QApplication(sys.argv)
example = App()
example.show()
sys.exit(application.exec_())
slider.py
from PyQt5 import uic
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from functools import partial
import inspect
class Slider(QSlider):
def __init__(self, parent=None):
super().__init__(parent)
self.valueChanged.connect(self.valueChange)
self.setMinimum(8)
self.setMaximum(64)
self.setTickInterval(8)
def valueChange(self):
pass
# Here I need to change App.canvas, have no idea how to do it
ui.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# 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(912, 715)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.sidebar = QtWidgets.QWidget(self.centralwidget)
self.sidebar.setMinimumSize(QtCore.QSize(388, 0))
self.sidebar.setAutoFillBackground(True)
self.sidebar.setObjectName("sidebar")
self.clearButton = QtWidgets.QPushButton(self.sidebar)
self.clearButton.setGeometry(QtCore.QRect(20, 30, 91, 41))
self.clearButton.setObjectName("clearButton")
# This is where Slider is called
# The file is generated so I can't edit it
self.brushSizeSlider = Slider(self.sidebar)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.brushSizeSlider.setGeometry(QtCore.QRect(20, 120, 160, 22))
self.brushSizeSlider.setOrientation(QtCore.Qt.Horizontal)
self.brushSizeSlider.setObjectName("brushSizeSlider")
self.brushSizeLabel = QtWidgets.QLabel(self.sidebar)
self.brushSizeLabel.setGeometry(QtCore.QRect(20, 90, 101, 16))
self.brushSizeLabel.setObjectName("brushSizeLabel")
self.brushColorDial = QtWidgets.QDial(self.sidebar)
self.brushColorDial.setGeometry(QtCore.QRect(20, 180, 50, 64))
self.brushColorDial.setObjectName("brushColorDial")
self.brushColorLabel = QtWidgets.QLabel(self.sidebar)
self.brushColorLabel.setGeometry(QtCore.QRect(20, 160, 101, 16))
self.brushColorLabel.setObjectName("brushColorLabel")
self.horizontalLayout.addWidget(self.sidebar)
self.canvas = Canvas(self.centralwidget)
self.canvas.setMinimumSize(QtCore.QSize(500, 500))
self.canvas.setMaximumSize(QtCore.QSize(5000, 5000))
self.canvas.setBaseSize(QtCore.QSize(500, 500))
self.canvas.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.canvas.setAutoFillBackground(True)
self.canvas.setStyleSheet("canvas{background-color: rgb(70, 70, 50);}")
self.canvas.setObjectName("canvas")
self.horizontalLayout.addWidget(self.canvas)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.clearButton.setText(_translate("MainWindow", "Clear canvas"))
self.brushSizeLabel.setText(_translate("MainWindow", "Current brush size:"))
self.brushColorLabel.setText(_translate("MainWindow", "Current brush color:"))
from canvas import Canvas
from slider import Slider

Related

PyQt5 multi-window

# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'PyosUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget
)
class GameWindow(QWidget):
def __init__(self):
super.__init__()
layout = QVBoxLayout()
self.label = QLabel("Hello!")
layout.addWidget(self.label)
self.setLayout(layout)
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.setEnabled(True)
Dialog.resize(1044, 601)
self.Notepad = QtWidgets.QPushButton(Dialog)
self.Notepad.setGeometry(QtCore.QRect(50, 520, 281, 61))
self.Notepad.setObjectName("Notepad")
self.Game = QtWidgets.QPushButton(Dialog)
self.Game.setGeometry(QtCore.QRect(390, 520, 271, 61))
self.Game.setObjectName("Game")
self.Poweroff = QtWidgets.QPushButton(Dialog)
self.Poweroff.setGeometry(QtCore.QRect(690, 520, 111, 31))
self.Poweroff.setObjectName("Poweroff")
self.Settings = QtWidgets.QPushButton(Dialog)
self.Settings.setGeometry(QtCore.QRect(690, 560, 111, 31))
self.Settings.setObjectName("Settings")
self.Date_time = QtWidgets.QDateTimeEdit(Dialog)
self.Date_time.setGeometry(QtCore.QRect(840, 20, 194, 22))
self.Date_time.setObjectName("Date_time")
self.label = QtWidgets.QLabel(Dialog)
self.label.setGeometry(QtCore.QRect(0, -20, 1051, 641))
self.label.setText("")
self.label.setPixmap(QtGui.QPixmap("background.png"))
self.label.setObjectName("label")
self.label.raise_()
self.Notepad.raise_()
self.Game.raise_()
self.Poweroff.raise_()
self.Settings.raise_()
self.Date_time.raise_()
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
l = QVBoxLayout()
self.Game.clicked.connect(self.button_clicked)
l.addWidget(self.Game)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "PyOS"))
self.Notepad.setText(_translate("Dialog", "Notepad"))
self.Game.setText(_translate("Dialog", "Game"))
self.Poweroff.setText(_translate("Dialog", "Poweroff"))
self.Settings.setText(_translate("Dialog", "Settings"))
def game_window(self):
game_window = QtWidgets.QDialog()
game_window.setWindowTitle("Game")
game_window.resize(600, 600)
game_window.show()
def button_clicked(self):
self.game_window()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
The above code is the UI I made with Pyqt5 designer. For the work I want to make, when the 'Game' button is clicked, a window dedicated to the 'Game' button should appear.
However, when I run the code above, when I press the 'Game' button, a 600 x 600 window appears and then disappears.
Why?
How else can I solve this problem?
Thank you.
The game_window is a local variable within the game_window() method in Ui_Dialog.
Set the window as an instance and it should be fine.
Modified method
# other code remains same.
def game_window(self):
self._game_window = QtWidgets.QDialog()
self._game_window.setWindowTitle("Game")
self._game_window.resize(600, 600)
self._game_window.show()
Edit: The reason to add the underscore (_) is to avoid having the same name as the method name.

QTextEdit clickable text

I have a log I have created with a simple [Add] text near each line which I want to be able to press and have it run a function that will know which line/text it has in this line.
I can also use QTextBrowser if needed for this.
My code:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(401, 308)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.chat_log = QtWidgets.QTextEdit(self.centralwidget)
self.chat_log.setGeometry(QtCore.QRect(10, 10, 381, 241))
self.chat_log.setReadOnly(True)
self.chat_log.setObjectName("chat_log")
MainWindow.setCentralWidget(self.centralwidget)
class MainFrame(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainFrame, self).__init__(parent)
self.setupUi(self)
def appending(self):
self.chat_log.append("Somethingsomething [Add]")
self.chat_log.append("Hello[Add]")
self.chat_log.append("What is up [Add]")
self.chat_log.append("Big boy [Add]")
if __name__ == "__main__":
app = QApplication(sys.argv)
form = MainFrame()
form.show()
form.appending()
app.exec_()
What I want is basically when someone pressed "[Add]" it will know which line it pressed and will print the text in that line
Say I pressed the first line it will then print("Somethingsomething")
You need to make your [Add] texts clickable, the easiest way to do so is using HTML markup, then you just need to identify the text you had been clicking on:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class MyTextEdit(QtWidgets.QTextEdit):
def mousePressEvent(self, e):
self.link = self.anchorAt(e.pos())
def mouseReleaseEvent(self, e):
if self.link:
print(f"Clicked on {self.link}")
self.link = None
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(401, 308)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.chat_log = MyTextEdit(self.centralwidget)
self.chat_log.setGeometry(QtCore.QRect(10, 10, 381, 241))
self.chat_log.setReadOnly(True)
self.chat_log.setObjectName("chat_log")
MainWindow.setCentralWidget(self.centralwidget)
class MainFrame(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainFrame, self).__init__(parent)
self.setupUi(self)
def appending(self):
messages = ["Somethingsomething", "Hello", "What is up", "Big bo"]
for msg in messages:
self.chat_log.append(
f'<span>{msg}<a style="color: pink" href="{msg}">[Add]</a></span>'
)
if __name__ == "__main__":
app = QApplication(sys.argv)
form = MainFrame()
form.show()
form.appending()
app.exec_()
Out:
Clicked on Big bo
Clicked on Hello
Clicked on Somethingsomething

PyQt5 - not able to add elements to QTabWidget

I am trying to add a second tab (containing a pie chart visualization) to my PyQt5 GUI, but I cannot figure out how to display it. This second tab is laid out in a separate class and my program has the following structure:
main.py
from PyQt5 import QtWidgets, QtCore
from gui import UiMainWindow
import sys
class Logic(QtWidgets.QMainWindow, UiMainWindow,):
def __init__(self):
super().__init__()
self.setupUi(self)
self.treeView = QtWidgets.QTreeView(self.tabwidget.main_tab)
self.treeView.setGeometry(QtCore.QRect(270, 90, 801, 571))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
logic = Logic()
logic.show()
sys.exit(app.exec_())
gui.py
from PyQt5 import QtWidgets, QtCore
from main_tab import MainTab
class UiMainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("Invoice Manager")
MainWindow.resize(1120, 750)
MainWindow.setTabShape(QtWidgets.QTabWidget.Rounded)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.tabwidget = MainTab(self.centralwidget)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
self.tabwidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("Test", "Test"))
self.tabwidget.setTabText(
self.tabwidget.indexOf(self.tabwidget.main_tab),
_translate("MainWindow", "Main"))
self.tabwidget.setTabText(
self.tabwidget.indexOf(self.tabwidget.visual_tab),
_translate("MainWindow", "Tab_2"))
main_tab.py
from PyQt5 import QtCore, QtWidgets
from tab_2 import Tab2
class MainTab(QtWidgets.QTabWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setGeometry(QtCore.QRect(0, 0, 1120, 750))
self.main_tab = QtWidgets.QWidget()
self.addTab(self.main_tab, "")
self.visual_tab = Tab2()
self.addTab(self.visual_tab, "")
tab_2.py
from PyQt5 import QtWidgets
from PyQt5.QtChart import QChart, QChartView, QPieSeries, QPieSlice
from PyQt5.QtGui import QPainter, QPen
from PyQt5.QtCore import Qt
class Tab2(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
self.create_piechart()
def create_piechart(self):
series = QPieSeries()
series.append("Label 1", 2)
series.append("Label 2", 2)
series.append("Label 3", 2)
pie_slice = QPieSlice()
pie_slice.setExploded(True)
pie_slice.setLabelVisible(True)
pie_slice.setPen(QPen(Qt.darkGreen, 2))
pie_slice.setBrush(Qt.green)
chart = QChart()
chart.legend().hide()
chart.addSeries(series)
chart.createDefaultAxes()
chart.setAnimationOptions(QChart.SeriesAnimations)
chart.setTitle("Pie Chart Example")
chart.legend().setVisible(True)
chart.legend().setAlignment(Qt.AlignBottom)
chartview = QChartView(chart)
chartview.setRenderHint(QPainter.Antialiasing)
I have been trying various solutions, but no luck so far. Any help would be highly appreciated!

How to add some items or some pixmaps to the qgraphicscene?

I'm trying to make a puzzle game. My thought came from the 8-puzzle problem, so now I try to use the pyqt5. My opinion is that I choose a picture from my computer and then I try to use it. But when I try to upset the picture ,it comes some trouble.So I come here and look for help
since it comes to trouble so I simplify the code that just make the picture change into pieces. And now I just try them list on the qgraphicscene firstly, but I don't known why all of them stacked in the upper left corner, I just want them list in order which looks like the original picture, what should I do?
import sys
from vision import Ui_MainWindow
from PyQt5.QtWidgets import QMainWindow, QFileDialog
from PyQt5 import QtWidgets, QtGui, QtCore
from PIL import Image
class MyWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.setupUi(self)
self.item = []
self.if_opened = 0
self.dim = 3
self.upset.clicked.connect(self.make_upset)
def make_upset(self):
if self.image is None:
return
self.scene.clear()
self.showView.setScene(self.scene)
pixmap = QtGui.QPixmap(self.image)
x_side = self.image_file.size[0] / self.dim
y_side = self.image_file.size[1] / self.dim
count = 0
self.item.clear()
for j in range(self.dim):
for i in range(self.dim):
m = pixmap.copy(i * x_side, j * y_side, x_side, y_side)
self.item.append(QtWidgets.QGraphicsPixmapItem(m))
self.scene.addItem(self.item[count])
count = count + 1
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
myShow = MyWindow()
myShow.show()
sys.exit(app.exec_())
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1077, 741)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.upset = QtWidgets.QPushButton(self.centralwidget)
self.upset.setGeometry(QtCore.QRect(800, 210, 171, 51))
self.upset.setObjectName("upset")
self.showView = QtWidgets.QGraphicsView(self.centralwidget)
self.showView.setGeometry(QtCore.QRect(55, 38, 643, 623))
self.showView.setObjectName("showView")
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.upset.setText(_translate("MainWindow", "upset"))

In PyQt, image won't show after override mousePressEvent of QGraphicsView

I want to write a simple program shows a picture and print which pixel was clicked by override mousePressEvent of QGraphicsView.
When I don't override mousePressEvent of QGraphicsView, the image shows fine. But when I do override it, not only the position failed to show itself, the canvas become blank.
before override:
import sys
from PyQt5 import Qt
from PyQt5 import uic
a = Qt.QApplication(sys.argv)
from untitled import Ui_Form
# class override_graphicsView (Qt.QGraphicsView):
#
# def mousePressEvent(self, event):
# print(event.pos())
class Image_Process(Qt.QWidget):
def __init__(self):
super(Image_Process, self).__init__()
self.path = r"d:\test\winxp.jpg" #image path
self.new = Ui_Form()
self.new.setupUi(self)
# self.new.graphicsView = override_graphicsView()
self.pixmap = Qt.QPixmap()
self.pixmap.load(self.path)
self.pixmap = self.pixmap.scaled(self.size(), Qt.Qt.KeepAspectRatio)
self.graphicsPixmapItem = Qt.QGraphicsPixmapItem(self.pixmap)
self.graphicsScene = Qt.QGraphicsScene()
self.graphicsScene.addItem(self.graphicsPixmapItem)
self.new.graphicsView.setScene(self.graphicsScene)
my_Qt_Program = Image_Process()
my_Qt_Program.show()
sys.exit(a.exec_())
After I uncomment those lines, the canvas becomes this, and nothing was printed after click.
The untitled.py was generated from QtDesigner
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'untitled.ui'
#
# Created: Mon Jan 12 02:07:05 2015
# by: PyQt5 UI code generator 5.3.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(451, 286)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
Form.setSizePolicy(sizePolicy)
self.gridLayout = QtWidgets.QGridLayout(Form)
self.gridLayout.setObjectName("gridLayout")
self.graphicsView = QtWidgets.QGraphicsView(Form)
self.graphicsView.setObjectName("graphicsView")
self.gridLayout.addWidget(self.graphicsView, 0, 0, 1, 1)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
Helped by Plouff, I have solved my own problem.
First, I should override QGraphicsScene instead of QGraphicsView, and the following line should be called for QGraphicsScene to handle mousePressEvent
super(override_graphicsScene, self).mousePressEvent(event)
Modified code:
import sys
from PyQt5 import Qt
from PyQt5 import uic
a = Qt.QApplication(sys.argv)
from untitled import Ui_Form
# Override like this:
class override_graphicsScene (Qt.QGraphicsScene):
def __init__(self,parent = None):
super(override_graphicsScene,self).__init__(parent)
def mousePressEvent(self, event):
super(override_graphicsScene, self).mousePressEvent(event)
print(event.pos())
class Image_Process(Qt.QWidget):
def __init__(self):
super(Image_Process, self).__init__()
self.path = r"d:\test\winxp.jpg" #image path
self.new = Ui_Form()
self.new.setupUi(self)
self.pixmap = Qt.QPixmap()
self.pixmap.load(self.path)
self.pixmap = self.pixmap.scaled(self.size(), Qt.Qt.KeepAspectRatio)
self.graphicsPixmapItem = Qt.QGraphicsPixmapItem(self.pixmap)
self.graphicsScene = override_graphicsScene(self)
self.graphicsScene.addItem(self.graphicsPixmapItem)
self.new.graphicsView.setScene(self.graphicsScene)
my_Qt_Program = Image_Process()
my_Qt_Program.show()
sys.exit(a.exec_())
The program works fine.

Categories

Resources