How to tell if two QPushButtons have been pressed (Python) - python

I need to call a member function when both of my QPushButtons have been pressed. I cannot find a way to keep track of if they have been pressed.
I call a function when either of the buttons are clicked using ...clicked.connect(func) and within that function I have tried to: 1) return a value, 2) update a member variable. Below I have shown how I tried to create and update member variable and use a conditional to see if both variables were true so that I could call my next function.
def __init__(self, parent=None):
super(MorphingApp, self).__init__(parent)
self.setupUi(self)
self.startIm = None
self.endIm = None
self.initialState()
def initialState(self):
self.btn_loadStart.clicked.connect(self.loadImageS)
self.btn_loadEnd.clicked.connect(self.loadImageE)
if(self.startIm is True and self.endIm is True):
self.loadedState()
def initialState(self):
self.startIm = True
def loadImageE(self):
self.endIm = True
My functions of course do things, but I removed parts that were irrelevant. When I run the GUI I am able to load the images but the function that is supposed to be called after both buttons have been pushed is not called. I know this because the state of the GUI is not changing as I intend.
This is my first time posting a question so let me know how to improve :)

Use a simple True/False flag to check if the button has been pressed at least once. Both buttons start with the flag set to False, so we can set that up in the init method.
Then place the code that checks to see if both buttons have been pressed inside the functions connected to them. Finally, simply call the respective "final" function if the check passes.
This is an example using two generic buttons:
def __init__(self, parent=None):
super(MorphingApp, self).__init__(parent)
self.btn_01_pressed = False
self.btn_02_pressed = False
self.set_buttons()
def set_buttons(self):
self.btn_01.clicked.connect(self.check01)
self.btn_02.clicked.connect(self.check02)
def check01(self):
self.btn_01_pressed = True
if self.btn_01_pressed is True and self.btn_02_pressed is True:
self.call_final_function()
def check02(self):
self.btn_02_pressed = True
if self.btn_01_pressed is True and self.btn_02_pressed is True:
self.call_final_function()
def call_final_function(self):
# do something great here
Like in your post I've ommited some parts necessary for the actual code (like creating the QPushButton widgets), but hopefully you get the idea.

def __init__(self, parent=None):
super(MorphingApp, self).__init__(parent)
self.setupUi(self)
self.startIm = False
self.endIm = False
self.btn_loadStart.clicked.connect(self.loadImageS)
self.btn_loadEnd.clicked.connect(self.loadImageE)
def loadImageS(self):
self.startIm = True
if self.startIm and self.endIm:
self.loadedState()
def loadImageE(self):
self.endIm = True
if self.startIm and self.endIm:
self.loadedState()

Related

setText changes every QTextEdit even if specified to focus on one

I'm trying to put text into a QTextEdit in PyQT5, but every time I set the text for one text field it copies the same value into the other text fields on the same page. Even though I specified that it has to only change the contents of the QTextEdit field of which I have given the ID. Is this normal behavior, or is there a workaround? Any advice would be appreciated.
class NodeUIDScanner(QObject):
rfidTag = pyqtSignal(str)
scanEnabled = bool
def init(self):
self.scanEnabled = False
def run(self):
self.scanEnabled = True
dev = rfid_scanner.init('COM7') #change depending on usb port
while self.scanEnabled:
rfid = rfid_scanner.read(dev)
if rfid is None:
continue
if 'rfid' not in rfid:
continue
rfid = rfid['rfid']
self.scanEnabled = False
self.rfidTag.emit(rfid)
def stop(self):
self.scanEnabled = False
class Main:
def __init__(self):
self.main_win = QMainWindow()
self.ui = RFIDScannerDesign.Ui_MainWindow()
self.ui.setupUi(self.main_win)
#default page on load is the create page
self.ui.stackedWidget.setCurrentWidget(self.ui.pageCreate)
#nav buttons
self.ui.buttonCreate.clicked.connect(self.showCreate)
self.ui.buttonRead.clicked.connect(self.showRead)
self.ui.buttonUpdate.clicked.connect(self.showUpdate)
self.ui.buttonDelete.clicked.connect(self.showDelete)
self.ui.read_editButton.clicked.connect(self.showEdit)
#scan buttons
self.ui.create_buttonScan.clicked.connect(self.scanNodeUidCreate)
self.ui.read_buttonScan.clicked.connect(self.scanNodeUidRead)
self.ui.update_buttonScanOld.clicked.connect(self.scanNodeUidOld)
self.ui.update_buttonScanNew.clicked.connect(self.scanNodeUidNew)
self.ui.delete_buttonScan.clicked.connect(self.scanNodeUidDelete)
#submit button gives alert asking if you're sure you have the right input
self.ui.create_buttonSubmit.clicked.connect(self.alertCreate)
self.ui.edit_buttonSubmit.clicked.connect(self.alertEdit)
self.ui.update_buttonSubmit.clicked.connect(self.alertUpdate)
self.ui.delete_buttonSubmit.clicked.connect(self.alertDelete)
#yes or no buttons with alert. Yes sends info to db. No keeps you on the page
self.ui.create_buttonSubmitYes.clicked.connect(self.submitCreate)
self.ui.create_buttonSubmitNo.clicked.connect(self.hideCreateAlert)
self.ui.edit_buttonSubmitYes.clicked.connect(self.submitEdit)
self.ui.edit_buttonSubmitNo.clicked.connect(self.hideEditAlert)
self.ui.update_buttonSubmitYes.clicked.connect(self.submitUpdate)
self.ui.update_buttonSubmitNo.clicked.connect(self.hideUpdateAlert)
self.ui.delete_buttonSubmitYes.clicked.connect(self.submitDelete)
self.ui.delete_buttonSubmitNo.clicked.connect(self.hideDeleteAlert)
#define QThread here so it can be used
self.main_win.thread = QThread()
self.worker = NodeUIDScanner()
self.worker.moveToThread(self.main_win.thread)
self.main_win.thread.started.connect(self.worker.run)
def show(self):
self.main_win.show()
#Functions for nav buttons to show the right page
def showCreate(self):
self.ui.stackedWidget.setCurrentWidget(self.ui.pageCreate)
self.ui.create_buttonSubmit.show()
self.ui.create_alert.hide()
self.ui.create_buttonSubmitYes.hide()
self.ui.create_buttonSubmitNo.hide()
def showRead(self):
self.ui.stackedWidget.setCurrentWidget(self.ui.pageRead)
self.ui.read_editButton.show()
def showUpdate(self):
self.ui.stackedWidget.setCurrentWidget(self.ui.pageUpdate)
self.ui.update_buttonSubmit.show()
self.ui.update_alert.hide()
self.ui.update_buttonSubmitYes.hide()
self.ui.update_buttonSubmitNo.hide()
def showDelete(self):
self.ui.stackedWidget.setCurrentWidget(self.ui.pageDelete)
self.ui.delete_buttonSubmit.show()
self.ui.delete_alert.hide()
self.ui.delete_buttonSubmitYes.hide()
self.ui.delete_buttonSubmitNo.hide()
def showEdit(self):
self.ui.stackedWidget.setCurrentWidget(self.ui.pageEdit)
self.ui.edit_buttonSubmit.show()
self.ui.edit_alert.hide()
self.ui.edit_buttonSubmitYes.hide()
self.ui.edit_buttonSubmitNo.hide()
#Threads for scanning nodeUIDs asynchronously
def readRFID(self, rfid):
try:
response_rfid = database.query(IndexName='rfid-index',
KeyConditionExpression=Key('rfid').eq(rfid))
if response_rfid['Count'] > 0:
print('RFID already in database', rfid)
print('Node uid:', int(response_rfid['Items'][0]['node_uid']))
return int(response_rfid['Items'][0]['node_uid'])
except Exception as e:
print(e)
def scanNodeUidCreate(self):
if self.ui.create_buttonScan.clicked:
self.worker.stop()
self.worker.rfidTag.connect(self.displayNodeCreate)
self.main_win.thread.start()
def scanNodeUidRead(self):
if self.ui.read_buttonScan.clicked:
self.worker.stop()
self.worker.rfidTag.connect(self.displayNodeRead)
self.main_win.thread.start()
def scanNodeUidOld(self):
if self.ui.update_buttonScanOld.clicked:
self.worker.stop()
self.worker.rfidTag.connect(self.displayNodeOld)
self.main_win.thread.start()
def scanNodeUidNew(self):
if self.ui.update_buttonScanNew.clicked:
self.worker.stop()
self.worker.rfidTag.connect(self.displayNodeNew)
self.main_win.thread.start()
def scanNodeUidDelete(self):
if self.ui.delete_buttonScan.clicked:
self.worker.stop()
self.worker.rfidTag.connect(self.displayNodeDelete)
self.main_win.thread.start()
#Slot for passing signal from worker thread to keep it asynchronous
def displayNodeCreate(self, node):
if self.ui.stackedWidget.currentWidget() == self.ui.pageCreate:
self.ui.create_nodeUid.setText(str(self.readRFID(node)))
self.ui.create_buttonScan.setChecked(False)
self.worker.stop()
self.main_win.thread.quit()
self.main_win.thread.wait()
def displayNodeRead(self, node):
if self.ui.stackedWidget.currentWidget() == self.ui.pageRead:
self.ui.read_nodeUid.setText(str(self.readRFID(node)))
self.ui.edit_nodeUid.setText(str(self.readRFID(node)))
self.ui.read_buttonScan.setChecked(False)
self.worker.stop()
self.main_win.thread.quit()
self.main_win.thread.wait()
def displayNodeOld(self, node):
if self.ui.stackedWidget.currentWidget() == self.ui.pageUpdate:
self.ui.update_nodeUidOld.setText(str(self.readRFID(node)))
self.ui.update_buttonScanOld.setChecked(False)
self.worker.stop()
self.main_win.thread.quit()
self.main_win.thread.wait()
def displayNodeNew(self, node):
if self.ui.stackedWidget.currentWidget() == self.ui.pageUpdate:
self.ui.update_nodeUidNew.setText(str(self.readRFID(node)))
self.ui.update_buttonScanNew.setChecked(False)
self.worker.stop()
self.main_win.thread.quit()
self.main_win.thread.wait()
def displayNodeDelete(self, node):
if self.ui.stackedWidget.currentWidget() == self.ui.pageDelete:
self.ui.delete_nodeUid.setText(str(self.readRFID(node)))
self.ui.delete_buttonScan.setChecked(False)
self.worker.stop()
self.main_win.thread.quit()
self.main_win.thread.wait()
#Submit button, when pressed gives alerts
def alertCreate(self):
self.ui.create_buttonSubmit.hide()
self.ui.create_alert.show()
self.ui.create_buttonSubmitYes.show()
self.ui.create_buttonSubmitNo.show()
def alertEdit(self):
self.ui.edit_buttonSubmit.hide()
self.ui.edit_alert.show()
self.ui.edit_buttonSubmitYes.show()
self.ui.edit_buttonSubmitNo.show()
def alertUpdate(self):
self.ui.update_buttonSubmit.hide()
self.ui.update_alert.show()
self.ui.update_buttonSubmitYes.show()
self.ui.update_buttonSubmitNo.show()
def alertDelete(self):
self.ui.delete_buttonSubmit.hide()
self.ui.delete_alert.show()
self.ui.delete_buttonSubmitYes.show()
self.ui.delete_buttonSubmitNo.show()
#TODO add db connection
def submitCreate(self):
self.showCreate()
self.ui.create_alertSucces.show()
self.fade(self.ui.create_alertSucces)
def submitEdit(self):
self.showRead()
self.ui.read_alertSucces.show()
self.fade(self.ui.read_alertSucces)
def submitUpdate(self):
self.showUpdate()
self.ui.update_alertSucces.show()
self.fade(self.ui.update_alertSucces)
def submitDelete(self):
self.showDelete()
self.ui.delete_alertSucces.show()
self.fade(self.ui.delete_alertSucces)
def hideCreateAlert(self):
self.ui.create_buttonSubmit.show()
self.ui.create_alert.hide()
self.ui.create_buttonSubmitYes.hide()
self.ui.create_buttonSubmitNo.hide()
def hideEditAlert(self):
self.ui.edit_buttonSubmit.show()
self.ui.edit_alert.hide()
self.ui.edit_buttonSubmitYes.hide()
self.ui.edit_buttonSubmitNo.hide()
def hideUpdateAlert(self):
self.ui.update_buttonSubmit.show()
self.ui.update_alert.hide()
self.ui.update_buttonSubmitYes.hide()
self.ui.update_buttonSubmitNo.hide()
def hideDeleteAlert(self):
self.ui.delete_buttonSubmit.show()
self.ui.delete_alert.hide()
self.ui.delete_buttonSubmitYes.hide()
self.ui.delete_buttonSubmitNo.hide()
#fade effect for animations on labels and alerts
def fade(self, widget):
self.effect = QGraphicsOpacityEffect()
widget.setGraphicsEffect(self.effect)
self.animation = QtCore.QPropertyAnimation(self.effect, b"opacity")
self.animation.setDuration(3000)
self.animation.setStartValue(1)
self.animation.setEndValue(0)
self.animation.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
main_win = Main()
main_win.show()
sys.exit(app.exec_())
The problem is that every time a "buttonScan" is clicked, you connect it to a slot. Signals can be connected to an indefinite number of functions, and it is also possible to connect a signal to the same function more than once.
Stopping the worker won't change anything, as the object still exists, and when the signal will be emitted every (previously) connected function will be called as well.
Since all functions do practically the same thing, a better choice is to connect the signal to a single function, and set the target widgets when switching between pages.
Note that there are other problems in your code, most importantly:
clicked is a signal, using if someObject.someSignal: is pointless as it will always be considered valid, and since the function is already connected to that signal there would be no point in checking it anyway;
calling wait() of an external thread in the main thread is wrong, since it is blocking; I won't address the issue here as it's off topic to the question;
Unfortunately, your code is quite convoluted, so I'm only providing modifications for a single page, then you have to implement the rest on your own.
class Main:
def __init__(self):
# ...
self.ui.create_buttonScan.clicked.connect(self.startScan)
# ...
self.worker.rfidTag.connect(self.rfidReceived)
def showCreate(self):
if self.ui.stackedWidget.currentWidget == self.ui.pageCreate:
return
# ...
self.worker.stop() # just to be sure...
self.targetField = self.ui.create_nodeUid
self.targetCheck = self.ui.create_buttonScan
def startScan(self):
self.worker.stop()
self.main_win.thread.start()
def rfidReceived(self, node):
self.targetField.setText(str(self.readRFID(node))
self.targetCheck.setChecked(False)
self.worker.stop()
self.main_win.thread.quit()
self.main_win.thread.wait()
Note that more than half of your code is boilerplate just like the functions that update the text edits: they always do the same thing, just for different targets. This makes your code unnecessarily long and convoluted, so, prone to errors and annoying editing whenever you need to change its behavior. Since it seems clear that almost all your pages have the same interface (except for the read page) it's pointless to have functions that do the same thing repeated 5 times, especially considering that you're using very similar names that might be easily confused; each function is repeated for its 5 "targets" (the page and its buttons), that makes 30 functions, while you could just have 4 or 5. A better modularization and usage of classes will certainly be a smarter choice, will cut down your code by at least half the size, and will make debugging and maintenance much easier.

Python Kivy: How to check if a button is clicked or not?

I have to create multiple buttons (for each element in a given list). For this I'm using for loops. The button that is pressed, must be recorded. However, the value of self.i is not stored at that particular instance, the last value (in this case - 2 is stored, after the loop is completed). How can I solve this error? Is there a way to add conditional statements to check if the button is clicked or not, and if it is, the value of self.i is stored in another variable.
class InfoPage(GridLayout):
def __init__(self, **kwargs):
super(InfoPage, self).__init__(**kwargs)
self.cols = 1
self.peers = ["ishika", "nate", "nf"]
for self.i in range(3):
self.peers[self.i] = Button(text=peers[self.i], id=str(self.i))
self.add_widget(self.peers[self.i])
self.peers[self.i].bind(on_press=self.add)
def add(self, _):
id_of_button_pressed = self.peers[self.i].id
print(id_of_button_pressed)
Your add() method is using self.i, which will be the last value that self.i was set to in the __init__() method (2). So you just need to modify your add() method to something like:
def add(self, button):
id_of_button_pressed = button.id
print(id_of_button_pressed)

Questions about python module

It is my first time learning PyQt5.
While I read code related to PyQt5, I have a question some modules.
Is 'isStarted' a variable or in a module?
def __init__(self):
super().__init__()
self.isStarted = False
self.isPaused = False
self.nextMove = None
self.lastShape = Shape.shapeNone
def __init__(self):
super().__init__()
self.isStarted = False
self.isPaused = False
self.nextMove = None
self.lastShape = Shape.shapeNone
Whenever there is an =, rest assured that anything on the left side is a variable. In this case, you are making isStarted variable an attribute of self which is the class in which your code is in. So you can call it from other classes like: print(App.isStarted) (of course, replace App with whatever your class is named.

Binding checkbox to string in IronPython

How can I bind a checkbox to a string such that when the checkbox is checked/unchecked, the value of the string changes? I have this (with CheckAll as my checkbox):
class MyWindow(Window):
def __init__(self):
wpf.LoadComponent(self, 'BioApp1.xaml')
openDialog = SequenceFileOperations()
self.Sequences = openDialog.Open()
object = MyObjects(self.Sequences)
self.CheckAll.DataContext = object
self.IDLabel.DataContext = object
class MyObjects(object):
def __init__(self, Sequences):
self.CurrentSeq = Sequences[0]
self.ID = self.CurrentSeq.ID
and
<Label Height="28" HorizontalAlignment="Left" Margin="152,221,0,0" VerticalAlignment="Top" Width="98" Name="IDLabel" Content="{Binding Path=ID}"/>
I want that when the checkbox is unchecked, the label should display the sequence ID, but when it is checked, it should simply display “All”. For this I need to change the ID property of CurrentSeq to “All”. How do I do that by data binding? Is there any other way I can do this?
EDIT: I feel really stupid but I just can’t get this to work. I have been trying to follow the suggestion about using getter/setter but I guess I don’t know enough. Before doing anything more complicated, I simply want to make a button disabled when I tick the checkbox and enable it when I uncheck it. This is what I wrote:
class MyWindow(Window):
def __init__(self):
wpf.LoadComponent(self, 'App1.xaml')
object = BindingClass(self.Check, self.PreviousBtn)
self.PreviousBtn.DataContext = object
class BindingClass(object):
def __init__(self, Check, PreviousBtn):
self.Check = Check
self.PreviousBtn = PreviousBtn
def GetEnabledConverter(self):
if self.CheckAll.IsChecked:
return self.PreviousBtn.IsEnabled
def SetEnabledConverter(self):
if self.CheckAll.IsChecked:
self.PreviousBtn.IsEnabled = False
else:
self.PreviousBtn.IsEnabled = True
EnabledConverter = property(GetEnabledConverter, SetEnabledConverter)
And:
<Button Content="Previous" IsEnabled="{Binding Path=EnabledConverter}" />
Unfortunately there is no error but no effect either. The code does not do anything. Would really appreciate if you could help me out with this.
EDIT2: Using the notify_property, I tried this:
class MyWindow(Window):
def __init__(self):
wpf.LoadComponent(self, 'Test.xaml')
c = Converters(self.check1, self.Button)
self.Button.DataContext = c
class Converters(NotifyPropertyChangedBase):
def __init__(self, check, button):
super(Converters, self).__init__()
self.Check = check
self.Button = button
#notify_property
def ButtonEnabled(self):
return self.Button.IsEnabled
#ButtonEnabled.setter
def ButtonEnabled(self):
if self.Check.IsChecked:
self.Button.IsEnabled = False
else:
self.Button.IsEnabled = True
Still the same result: no effect. I just cannot understand where the problem is.
I would use Converter.
Edit:
You can implement converter in Python:
class BoolToVisibilityConverter(IValueConverter):
def Convert(self, value, targetType, parameter, culture):
return Visibility.Visible if value != val else Visibility.Collapsed
Last time I worked with WPF in IronPython, you could not use it directly in .xaml. I am not sure whether it has improved in 2.7.
Another possibility is to add another property which does the conversion (converted_ID) in its setter/getter. Thinking more about it, I would do rather this, because the code is in one place.
Edit 2:
Make sure, you are using notify_property instead of classic Python property.

Is it possible to make an accordion style check list in wxPython?

I want to make a checklist with a accordion style in a wxPython widget. I know about checklistbox, but I couldn't find anything in the official docs concerning it. Has anyone done this?
You can use a wx.combo.ComboCtrl which allows any custom popup, and combine this with a wx.CheckListBox.
Here's what is could look like, folded:
and unfolded:
To get this, I started with the ComboCtrl example in the demo, and in the ListCtrlComboPopup list class I replaced ListCtrl with CheckListBox everywhere (and made a few other small changes to make the commands consistent with a CheckListBox control rather than a ListCtrl).
You have to use a wx.PreCheckListBox() for the two part initialization that is required for this.
Here's my implementation. Combine this with a ComboCtrl and you're all set.
import wx
from wx import combo
class checkListComboPopup(combo.ComboPopup):
def __init__(self):
combo.ComboPopup.__init__(self)
self.checklist = wx.PreCheckListBox()
self._value = -1
def Init(self):
self._value = -1
def Create(self, parent):
return self.checklist.Create(parent, 1, wx.Point(0,0), wx.DefaultSize)
def GetControl(self):
return self.checklist
def SetStringValue(self, s):
pass
def GetStringValue(self):
if (self._value >= 0):
return self.checklist.GetItemText(self, self._value)
else:
return wx.EmptyString
def OnMouseMove(self, event):
pass
def GetPreCheckList(self):
return self.checklist
def OnMouseClick(self, event):
pass
Two part creation

Categories

Resources