Python passing QtDesigner class as dynamic argument to class in external file - python

I want to be able to pass in any QT class object between two Python classes in separate *.py files. I researched this on-line and within SO before posting. I'm not that skilled in OO or Python.
First py file was created in QT_designer (5.11): rs485ConfigMenu with class Ui_rs485ConfigMenu(object), with from PyQt5 import QtCore, QtGui, QtWidgets. Not touching that code.
I wrote a second python (python 3.5) file with a class that should accept the Ui_rs485ConfigMenu. I know that the second argument for the class is wrong, but I don't know how to pass in the Ui class for any Ui object as needed (e.g., Ui_menu01 vs. Ui_menu02, etc.).
My code for the second, separate python file and its class is:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
widgetList = [ "QPushButton", "QCheckBox", "QDialogButtonBox", "QLineEdit", "QRadioButton", "QComboBox", "QLabel"]
class widgetListMethod( QMainWindow, Ui_menu ):
def __init__(self):
super( widgetListMethod, self ).__init__()
# Ui_mainMenu.__init__(self)
# self.setupUi(self)
def iterThroughWidgets(self):
menuWidgets = {}
i = 0
b = self.mainMenuFrame.children()
#need general menuFrame name for all menus
#need way of checking all instances in widgetList
for w in b:
flag1 = 0
string0 = str(type(w))
string1 = string0.split("PyQt5.QtWidgets.")
string2 = string1[1]
string3 = string2.split("'")
if not set(string3).isdisjoint(widgetList):
widgetText = w.text()
labelValue = widgetText.split("_value")
if isinstance( w, QLabel ) and len( labelValue ) > 1:
#keep only those QLabels that have .value in label
i = i + 1
flag1 = 1
elif not isinstance( w, QLabel ):
i = i + 1
flag1 = 1
if flag1 == 1:
x = w.geometry().x()
y = w.geometry().y()
menuWidgets[i] = [x, y, widgetText, w ]
return menuWidgets
Ui code from QtDesigner alone exceeds length for characters of 3000 by another 3000+. Cannot paste Ui and py code. Happy to send on or provide link from safe-site.

Related

Text-Based adventure in Kivy, Python

I'm very new to programming, just have an introductory seminar in university, and I'm supposed to program a little app in Python. For that, I want to use Kivy, but I got stuck.
I have a text file which should include the question, the possible answers and where it's supposed to go considering which answer the user chose:
0|"Will you rather do A or B?"|"A"|"B"|1|2
1|"Congrats, you chose A. Now go to B."|"Go to B"|"Go to B"|2|2
2|"That's B. Incredible. Want to discover C?"|"Yes."|"Stay here."|3|6
3|Wow, C is so cool, isn't it? There's also a D.|D? Crazy!|Boring. Go back.|4|0
4|Welcome to the depths of D. You are curious, aren't you?|Yep.|Nope.|5|0
5|Cool. There's nothing else here.|There must be.|Knew it.|4|0
6|Surprise! You should really discover C.|Alright.|Is there a D?|3|4
Now I want the game to go to the according line, replace the displayed text and go on. In theory, this is kind of working with my Code (I'm sorry if it's messed up, as I said, I'm new to this topic):
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
with open('try.txt') as t:
Lines = t.readlines()
first_line = Lines[0].strip()
x = first_line.split("|")
answer_b = int(x[5])
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.cols = 1
self.inside = GridLayout()
self.inside.cols = 2
self.btna = Button(text=x[2])
self.btna.bind(on_press=self.a)
self.inside.add_widget(self.btna)
self.btnb = Button(text=x[3])
self.btnb.bind(on_press=self.b)
self.inside.add_widget(self.btnb)
self.main_text = Label(text=x[1])
self.add_widget(self.main_text)
self.add_widget(self.inside)
def a(self, instance):
answer_a = int(x[4])
next_line_a = Lines[answer_a].strip()
print(next_line_a)
print(answer_a)
x = next_line_a.split("|")
self.main_text.text = x[1]
self.btna.text = x[2]
self.btnb.text = x[3]
self.btna.bind(on_press=self.a)
def b(self, instance):
next_line_b = Lines[answer_b].strip()
print(next_line_b)
print(answer_b)
x = next_line_b.split("|")
self.main_text.text = x[1]
self.btna.text = x[2]
self.btnb.text = x[3]
class Game(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
Game().run()
The problem is that it stays with the first line I defined and I don't really know how to go around that problem. I would imagine that I first define x with the first line, and after that x gets redefined with the according new line. But the next_line and x variable are both dependent on each other - I tried two different ways with answer a and b, but both don't really work. B will just continuously take the first_line-x, A tells me that x is referenced before assignment.
It would be great if someone could help me out of my confusion, because everything I tried just didn't work out...
Thanks!
I changed it so you pass items into the object that you create. It's challenging to get the inheritance correct.
I also added an initializer to the Games object. I think this works but to be honest I am not expert in the workings of Kivy and have gotten this pattern to work but I don't know for sure if it is best practice.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
with open('try.txt') as t:
Lines = t.readlines()
class MyGrid(GridLayout):
def __init__(self, Lines: list):
super(MyGrid, self).__init__()
self.first_line = Lines[0].strip()
self.xx = self.first_line.split("|")
self.answer_b = int(self.xx[5])
self.cols = 1
self.inside = GridLayout()
self.inside.cols = 2
self.btna = Button(text=self.xx[2])
self.btna.bind(on_press=self.a)
self.inside.add_widget(self.btna)
self.btnb = Button(text=self.xx[3])
self.btnb.bind(on_press=self.b)
self.inside.add_widget(self.btnb)
self.main_text = Label(text=self.xx[1])
self.add_widget(self.main_text)
self.add_widget(self.inside)
def a(self, instance):
answer_a = int(self.xx[4])
next_line_a = Lines[answer_a].strip()
print(next_line_a)
print(answer_a)
self.xx = next_line_a.split("|")
self.main_text.text = self.xx[1]
self.btna.text = self.xx[2]
self.btnb.text = self.xx[3]
self.btna.bind(on_press=self.a)
def b(self, instance):
next_line_b = Lines[self.answer_b].strip()
print(next_line_b)
print(self.answer_b)
self.xx = next_line_b.split("|")
self.main_text.text = self.xx[1]
self.btna.text = self.xx[2]
self.btnb.text = self.xx[3]
class Game(App):
def __init__(self, **kwargs):
self._arguments_to_pass_through = kwargs
super().__init__()
def build(self):
return MyGrid(**self._arguments_to_pass_through)
if __name__ == '__main__':
Game(Lines=Lines).run()

When I use "currentIndex()" on

I've been stuck on a school assignment for hours because of this one problem. I need to check the index number (AS AN INTEGER) for the currently selected item in a ListWidget.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QListView
from PyQt5.QtGui import QPixmap
import Ui_countries
class MyForm(QMainWindow, Ui_countries.Ui_mainWindow):
# DON'T TOUCH!
def __init__(self, parent=None):
super(MyForm, self).__init__(parent)
self.setupUi(self)
# END DON'T TOUCH
# EVENT HOOKS HERE
self.countryList.itemClicked.connect(self.CountrySelected)
self.actionLoad_Countries.triggered.connect(self.LoadCountries)
self.sqUnits.currentIndexChanged.connect(self.SqUnits)
#self.updatePopulation.itemClicked.connect(self.updateMemory)
# RESPONSES HERE
# def updateMemory(self):
def LoadCountries(self):
global namelist
global populationlist
global arealist
namelist = []
populationlist = []
arealist = []
objFile = open("GUI/countries.txt")
for line in objFile:
line = line.replace("\n","")
lineList = line.split(",")
self.countryList.addItem(lineList[0])
namelist.append(lineList[0])
populationlist.append(lineList[1])
arealist.append(lineList[2])
objFile.close()
def CountrySelected(self,selectedCountryIndex):
QMessageBox.information(self,"Country changed!",selectedCountryIndex.text())
strCountryName = selectedCountryIndex.text()
strCountryName = strCountryName.replace(" ", "_")
imagePixmap = QPixmap(f"GUI/Flags/{strCountryName}")
strCountryName = strCountryName.replace("_", " ")
self.lblCountryName.setText(strCountryName)
self.flag.setPixmap(imagePixmap)
self.flag.resize(imagePixmap.width(),imagePixmap.height())
idx = self.countryList.currentIndex()
# self.populationbox.setText(populationlist[idx])
# selectedCountryIndex.index()
#^^^^^^^^^^^ useful code
print(int(strCountryName))
def SqUnits(self):
QMessageBox.information(self,"Event Received","Please convert between different units.")
if self.sqUnits.currentText() == "Sq. Miles":
self.totalareabox.setText("YAAAAA")
else:
self.totalareabox.setText("YEEEE")
# DON'T TOUCH
if __name__ == "__main__":
app = QApplication(sys.argv)
the_form = MyForm()
the_form.show()
sys.exit(app.exec_())
The area to focus on would be the CountrySelected function. Whenever I try to run idx = self.countryList.currentIndex(), instead of an integer, I get 'PyQt5.QtCore.QModelIndex object at 0x051A6470' if I try to print idx. My instructor wants us to use pyqt, and I have no experience with it, so I'm kinda freaking out!
Figured it out, classmate sent me this: index = self.listWidget.currentRow()
No idea why index didn't work, but alas, tis the nature of doing your assignments one hour before they're due.

QCheckBox state change PyQt4

I'm trying to implement a system in PyQt4 where unchecking a checkbox would call function disable_mod and checking it would call enable_mod. But even though state is changing the checkboxes call the initial function they started with. For this case if an already checked box was clicked it'd always keep calling the disable_mod function! I don't understand why is this happening? Can you guys help me out here a little bit? Here's my code:
from PyQt4 import QtCore, QtGui
from os import walk
from os.path import join
import sys
def list_files_regex(dir):
l = []
for (root, dirnames, filenames) in walk(dir):
for d in dirnames:
list_files_regex(join(root, d))
l.extend(filenames)
return l
directory = "J:/test_pack"
directory = join(directory, "/mods")
count = 0
for y in list_files_regex(directory):
print y
count += 1
print count
class ModEdit(QtGui.QMainWindow):
def __init__(self, title, icon, x, y, w, h):
super(ModEdit, self).__init__()
self.setWindowTitle(title)
self.setWindowIcon(QtGui.QIcon(icon))
self.setGeometry(x, y, w, h)
self.choices = []
self.init()
def init(self):
scroll_widget = QtGui.QScrollArea()
sub_widget = QtGui.QWidget()
v_layout = QtGui.QVBoxLayout()
for y in list_files_regex(directory):
tmp = QtGui.QCheckBox(y, self)
tmp.resize(tmp.sizeHint())
if tmp.text()[len(tmp.text()) - 8: len(tmp.text())] != 'disabled':
tmp.setChecked(True)
# if tmp.isChecked() == 0:
# tmp.stateChanged.connect(self.enable_mod)
# if tmp.isChecked():
# tmp.stateChanged.connect(self.disable_mod)
# v_layout.addWidget(tmp)
self.choices.append(tmp)
print self.choices
for choice in self.choices:
v_layout.addWidget(choice)
if choice.isChecked():
choice.stateChanged.connect(self.disable_mod)
else:
choice.stateChanged.connect(self.enable_mod)
sub_widget.setLayout(v_layout)
scroll_widget.setWidget(sub_widget)
self.setCentralWidget(scroll_widget)
self.show()
def enable_mod(self):
print "ENABLE_TEST"
print self.choices[1].isChecked()
def disable_mod(self):
print "DISABLE_TEST"
print self.choices[1].isChecked()
def test(self):
print 'test'
for ch in self.choices:
if ch.isChecked():
ch.stateChanged.connect(self.disable_mod)
else:
ch.stateChanged.connect(self.enable_mod)
class Rename(QtCore.QObject):
enable = QtCore.pyqtSignal
disable = QtCore.pyqtSignal
app = QtGui.QApplication(sys.argv)
ex = ModEdit("Minecraft ModEdit", "ModEdit.png", 64, 64, 640, 480)
sys.exit(app.exec_())
The problem is that you're only connecting the checkbox stateChanged signal once during initialization. After the state of the checkbox changes, you're not disconnecting the signal and reconnecting it to the correct slot.
You'll need to connect the stateChanged signal to an intermediary slot that will decide which function to call based on the checkbox state. Since you're using the same slot for multiple checkboxes, it's probably best to also pass the checkbox to the slot as well.
from functools import partial
def init(self):
...
for tmp in list_of_checkboxes:
enable_slot = partial(self.enable_mod, tmp)
disable_slot = partial(self.disable_mod, tmp)
tmp.stateChanged.connect(lambda x: enable_slot() if x else disable_slot())
def enable_mod(self, checkbox):
print "ENABLE_TEST"
print checkbox.isChecked()
def disable_mod(self, checkbox):
print "DISABLE_TEST"
print checkbox.isChecked()
Alternatively, since we are now passing the checkbox to the slots, you could just use a single slot and check the checkbox state inside the slot
def init(self):
...
for tmp in list_of_checkboxes:
slot = partial(self.enable_disable_mod, tmp)
tmp.stateChanged.connect(lambda x: slot())
def enable_disable_mod(self, checkbox):
if checkbox.isChecked():
...
else:
...

QtSingleApplication for PySide or PyQt

Is there a Python version of the C++ class QtSingleApplication from Qt Solutions?
QtSingleApplication is used to make sure that there can never be more than one instance of an application running at the same time.
Here is my own implementation.
It has been tested with Python 2.7 and PySide 1.1.
It has essentially the same interface as the C++ version of QtSingleApplication. The main difference is that you must supply an application unique id to the constructor. (The C++ version by default uses the path to the executable as a unique id; that would not work here because the executable will most likely be python.exe.)
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtNetwork import *
class QtSingleApplication(QApplication):
messageReceived = Signal(unicode)
def __init__(self, id, *argv):
super(QtSingleApplication, self).__init__(*argv)
self._id = id
self._activationWindow = None
self._activateOnMessage = False
# Is there another instance running?
self._outSocket = QLocalSocket()
self._outSocket.connectToServer(self._id)
self._isRunning = self._outSocket.waitForConnected()
if self._isRunning:
# Yes, there is.
self._outStream = QTextStream(self._outSocket)
self._outStream.setCodec('UTF-8')
else:
# No, there isn't.
self._outSocket = None
self._outStream = None
self._inSocket = None
self._inStream = None
self._server = QLocalServer()
self._server.listen(self._id)
self._server.newConnection.connect(self._onNewConnection)
def isRunning(self):
return self._isRunning
def id(self):
return self._id
def activationWindow(self):
return self._activationWindow
def setActivationWindow(self, activationWindow, activateOnMessage = True):
self._activationWindow = activationWindow
self._activateOnMessage = activateOnMessage
def activateWindow(self):
if not self._activationWindow:
return
self._activationWindow.setWindowState(
self._activationWindow.windowState() & ~Qt.WindowMinimized)
self._activationWindow.raise_()
self._activationWindow.activateWindow()
def sendMessage(self, msg):
if not self._outStream:
return False
self._outStream << msg << '\n'
self._outStream.flush()
return self._outSocket.waitForBytesWritten()
def _onNewConnection(self):
if self._inSocket:
self._inSocket.readyRead.disconnect(self._onReadyRead)
self._inSocket = self._server.nextPendingConnection()
if not self._inSocket:
return
self._inStream = QTextStream(self._inSocket)
self._inStream.setCodec('UTF-8')
self._inSocket.readyRead.connect(self._onReadyRead)
if self._activateOnMessage:
self.activateWindow()
def _onReadyRead(self):
while True:
msg = self._inStream.readLine()
if not msg: break
self.messageReceived.emit(msg)
Here is a simple test program:
import sys
from PySide.QtGui import *
from QtSingleApplication import QtSingleApplication
appGuid = 'F3FF80BA-BA05-4277-8063-82A6DB9245A2'
app = QtSingleApplication(appGuid, sys.argv)
if app.isRunning(): sys.exit(0)
w = QWidget()
w.show()
app.setActivationWindow(w)
sys.exit(app.exec_())
You can have a look to this blog entry. It is for Pyside but I guess that it will work too with PyQt4.

Differentiating between signal sources in PySide

Is there trivial or elegant way to differentiate between many same-type signal sources in PySide/PyQt?
I am learning PySide. I have written simple application, which multiplies two numbers from two different QLineEdit() objects. Result is displayed in third QLineEdit.
Multiplier and multiplicand QLineEdit.textChanged() signals are connected to one method (TxtChanged). In this method i have to differentiate between signal sources. After some trials I figured out some workaround based upon placeholder text (4 lines below "is there another way?" comment in my code)
code:
import sys
from PySide import QtGui, QtCore
class myGUI(QtGui.QWidget):
def __init__(self, *args, **kwargs):
QtGui.QWidget.__init__(self, *args, **kwargs)
self.multiplier = 0
self.multiplicand = 0
self.myGUIInit()
def myGUIInit(self):
# input forms
a1_label = QtGui.QLabel("a1")
a1_edit = QtGui.QLineEdit()
a1_edit.setPlaceholderText("a1")
a2_label = QtGui.QLabel("a2")
a2_edit = QtGui.QLineEdit()
a2_edit.setPlaceholderText("a2")
# output form
a1a2_label = QtGui.QLabel("a1*a2")
self.a1a2_edit = QtGui.QLineEdit()
self.a1a2_edit.setReadOnly(True)
# forms events
a1_edit.textChanged.connect(self.TxtChanged)
a2_edit.textChanged.connect(self.TxtChanged)
# grid
grid = QtGui.QGridLayout()
grid.setSpacing(10)
grid.addWidget(a1_label,1,0)
grid.addWidget(a1_edit,1,1)
grid.addWidget(a2_label,2,0)
grid.addWidget(a2_edit,2,1)
grid.addWidget(a1a2_label,3,0)
grid.addWidget(self.a1a2_edit,3,1)
self.setLayout(grid)
self.setGeometry(100,100,200,200)
self.setWindowTitle("a*b")
self.show()
def TxtChanged(self,text):
sender = self.sender()
sender_text = sender.text()
if sender_text == '': sender_text = '0'
# is there another way?
if sender.placeholderText() == 'a1':
self.multiplicand = sender_text
else:
self.multiplier = sender_text
product = int(self.multiplier) * int(self.multiplicand)
print(self.multiplier,self.multiplicand,product)
self.a1a2_edit.setText(str(product))
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = myGUI()
sys.exit(app.exec_())
main()
best regards,
ostrzysz
You can use the functools.partial function - and therefore connect your signals to straight to your method/function but rather to a python object which will automatically call your function with some extra data you pass it:
from functools import partial
...
....
a1_edit.textChanged.connect(partial(self.TxtChanged, a1_edit))
a2_edit.textChanged.connect(partial(self.TxtChanged, a2_edit))
...
def TxtChanged(self,sender, text):
# and here you have the "sender" parameter as it was filled in the call to "partial"
...
partials is part of the stdlib, and is very readable, but one can always use lambda instead of partial for the same effect -
a1_edit.textChanged.connect(lambda text: self.TxtChanged(a1_edit, text))
In this way the object yielded by the lambda expression will be a temporary function that will use the values for "self" and "a1_edit" from the current local variables (at the time the button is clicked), and the variable named "text" will be supplied by Pyside's callback.
One thing that bugs me most in your code is that you are using placeholderText to differentiate. QObjects has another property called objectName that is more suitable for your task. And, you don't need to use sender.text() to get the text of QLineEdit. textChanged already sends it, so you will have it in your text parameter.
Also, using a dictionary instead of two separate variables (multiplier and multiplicand) will simplify your code further.
Here is the changed code:
class myGUI(QtGui.QWidget):
def __init__(self, *args, **kwargs):
QtGui.QWidget.__init__(self, *args, **kwargs)
self.data = {"multiplier": 0,
"multiplicand": 0}
self.myGUIInit()
def myGUIInit(self):
a1_label = QtGui.QLabel("a1")
a1_edit = QtGui.QLineEdit()
a1_edit.setObjectName("multiplicand")
a2_label = QtGui.QLabel("a2")
a2_edit = QtGui.QLineEdit()
a2_edit.setObjectName("multiplier")
# skipped the rest because same
def TxtChanged(self, text):
sender = self.sender()
# casting to int while assigning seems logical.
self.data[sender.objectName()] = int(text)
product = self.data["multiplier"] * self.data["multiplicand"]
print(self.data["multiplier"], self.data["multiplicand"], product)
self.a1a2_edit.setText(str(product))
Although #jsbueno and #Avaris answered your direct question about signal sources, I wouldn't relay on this sources in your concrete case. You can make instance members a1_edit and a2_edit:
...
self.a1_edit = QtGui.QLineEdit()
...
self.a2_edit = QtGui.QLineEdit()
...
It will simplify your TxtChanged function:
def TxtChanged(self,text):
try:
multiplier = int(self.a1_edit.text())
multiplicand = int(self.a2_edit.text())
except ValueError:
self.a1a2_edit.setText('Enter two numbers')
return
product = multiplier * multiplicand
print(multiplier, multiplicand, product)
self.a1a2_edit.setText(str(product))
Also, instead of handling ValueError exception, you can use QIntValidator for input controls:
self.int_validator = QtGui.QIntValidator()
self.a1_edit.setValidator(self.int_validator)
self.a2_edit.setValidator(self.int_validator)

Categories