I have a window, which has two QLineEdits in it. They are Line 1 and Line 2. I have a Backspace QPushButton which is to be pressed. I want some code which, when the backspace is pressed, will delete the text from the desired QLineEdit. This is to be done based on which one is focused at the time.
I understand that currently my code will backspace line1, however I want it to delete whichever line edit most recently had focus (i.e. if line1 was selected before backspace, it will get backspaced, if line 2 was the last in focus, then it will be backspaced).
I'm thinking it requires an if statement or 2, not sure though. How do I choose which line edit is deleted based on which one last had focus?
from PySide import QtGui, QtCore
from PySide.QtCore import*
from PySide.QtGui import*
class MainWindow(QtGui.QMainWindow): #The Main Window Class Maker
def __init__(self,):
QtGui.QMainWindow.__init__(self)
QtGui.QApplication.setStyle(('cleanlooks'))
mfont = QFont()
mfont.setFamily("BankGothic LT")
mfont.setPointSize(40)
mfont.setBold(True)
xfont = QFont()
xfont.setFamily("BankGothic LT")
xfont.setPointSize(40)
xfont.setLetterSpacing(QFont.AbsoluteSpacing, 15)
self.line1 = QLineEdit("Line 1", self)
self.line1.setFixedSize(460, 65)
self.line1.setFont(xfont)
self.line1.move(10,10)
self.line2 = QLineEdit("Line 2", self)
self.line2.setFixedSize(460, 65)
self.line2.setFont(xfont)
self.line2.move(10,200)
#BackSpace button
back = QPushButton("BackSpace", self)
back.move(100,100)
back.setFixedSize(300,75)
back.setFont(mfont)
back.clicked.connect(self.line1.backspace)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.setWindowTitle("BackSpace")
window.resize(480, 400)
window.setMaximumSize(480,400)
window.setMinimumSize(480,400)
window.show()
sys.exit(app.exec_())
You can accomplish this by utilizing the editingFinished signal and some manipulation of which line edit is connected to your backspace function.
I'll post then entire code block and then explain the changes I made below it.
class MainWindow(QtGui.QMainWindow):
def __init__(self,):
QtGui.QMainWindow.__init__(self)
QtGui.QApplication.setStyle(('cleanlooks'))
mfont = QFont()
mfont.setFamily("BankGothic LT")
mfont.setPointSize(40)
mfont.setBold(True)
xfont = QFont()
xfont.setFamily("BankGothic LT")
xfont.setPointSize(40)
xfont.setLetterSpacing(QFont.AbsoluteSpacing, 15)
self.line1 = QLineEdit("Line 1", self)
self.line1.setFixedSize(460, 65)
self.line1.setFont(xfont)
self.line1.move(10,10)
self.line2 = QLineEdit("Line 2", self)
self.line2.setFixedSize(460, 65)
self.line2.setFont(xfont)
self.line2.move(10,200)
self.recent_line = self.line2
self.previous_line = self.line1
#BackSpace button
self.back = QPushButton("BackSpace", self)
self.back.move(100,100)
self.back.setFixedSize(300,75)
self.back.setFont(mfont)
self.back.clicked.connect(self.recent_line.backspace)
self.line1.editingFinished.connect(self.last_lineedit)
self.line2.editingFinished.connect(self.last_lineedit)
def last_lineedit(self):
if isinstance(self.sender(), QLineEdit):
self.recent_line, self.previous_line = self.previous_line, self.recent_line
self.back.clicked.disconnect(self.previous_line.backspace)
self.back.clicked.connect(self.recent_line.backspace)
The first change that I've made is to include two new variables so that we can keep track of which QLineEdit was focused on last:
self.recent_line = self.line2
self.previous_line = self.line1
Next, I changed your back widget to be self.back, because we are going to need it outside of __init__
self.back = QPushButton("BackSpace", self)
self.back.move(100,100)
self.back.setFixedSize(300,75)
self.back.setFont(mfont)
self.back.clicked.connect(self.recent_line.backspace)
Then we are going to set up both line1 and line2 to the editingFinished signal.
This signal is emitted when the Return or Enter key is pressed or the line edit loses focus.
We'll be utilizing the "loses focus" part, because when the self.back button is pressed, the QLineEdit has lost focus.
Finally we get to the function that is going to keep track of which QLineEdit is connected to the backspace button at any given time.
def last_lineedit(self):
if isinstance(self.sender(), QLineEdit):
self.recent_line, self.previous_line = self.previous_line, self.recent_line
self.back.clicked.disconnect(self.previous_line.backspace)
self.back.clicked.connect(self.recent_line.backspace)
Within this function, we fist ensure that only one of the QLineEdits are sending the signal (just in case you connect something else to this signal that isn't a QLineEdit).
Next, we swap which QLineEdit was most recently focused on:
self.recent_line, self.previous_line = self.previous_line, self.recent_line
Then we disconnect from the previous line and connect to the new line. These last two lines are the magic that allows you to delete from both lines based on which had focus most recently. These lines are also why we changed to self.back, instead of leaving it at back. The locally scoped back wasn't accessible from the last_lineedit function.
It would be best if the backspace button didn't steal focus. That way, the caret will stay visible in the line-edit that has focus, and the user can easily see exactly what is happening. Doing it this way also makes the code much simpler:
back.setFocusPolicy(QtCore.Qt.NoFocus)
back.clicked.connect(self.handleBackspace)
def handleBackspace(self):
widget = QtGui.qApp.focusWidget()
if widget is self.line1 or widget is self.line2:
widget.backspace()
Ok guys, so I kept working on this in order to find a useful situation, where one would use this sort of functionality.
Say you were trying to create a login form for a touch screen, but you had no onscreen keyboard installed, you can 'create' one. This is what the intended use was for.
I've sort of tested this, and fixed any bugs I saw, but hey, feel free to use it. I noticed that heaps of examples existed for calculators, but no real examples existed for Keypad or Numberpad entry. Enjoy!
from PySide import QtGui, QtCore
from PySide.QtCore import*
from PySide.QtGui import*
class MainWindow(QtGui.QMainWindow): #The Main Window Class Maker
def __init__(self,):
QtGui.QMainWindow.__init__(self)
QtGui.QApplication.setStyle(('cleanlooks'))
U = QLabel("U:", self)
U.move(10,10)
P = QLabel("P:", self)
P.move(10,50)
self.line1 = QLineEdit("", self)
self.line1.move(20,10)
self.line1.setReadOnly(True)
self.line2 = QLineEdit("", self)
self.line2.move(20,50)
self.line2.setReadOnly(True)
self.line2.setEchoMode(QLineEdit.Password)
#PushButtons
back = QPushButton("<", self)
back.move(100,80)
back.setFocusPolicy(QtCore.Qt.NoFocus)
back.setFixedSize(20,20)
one = QPushButton('1', self)
one.move(10,80)
one.setFocusPolicy(QtCore.Qt.NoFocus)
one.setText("1")
one.setFixedSize(20,20)
two = QPushButton('2', self)
two.move(40,80)
two.setFocusPolicy(QtCore.Qt.NoFocus)
two.setFixedSize(20,20)
three = QPushButton('3', self)
three.move(70,80)
three.setFocusPolicy(QtCore.Qt.NoFocus)
three.setFixedSize(20,20)
four = QPushButton('4', self)
four.move(10,110)
four.setFocusPolicy(QtCore.Qt.NoFocus)
four.setFixedSize(20,20)
five = QPushButton('5', self)
five.move(40,110)
five.setFocusPolicy(QtCore.Qt.NoFocus)
five.setFixedSize(20,20)
six = QPushButton('6', self)
six.move(70,110)
six.setFocusPolicy(QtCore.Qt.NoFocus)
six.setFixedSize(20,20)
seven = QPushButton('7', self)
seven.move(10,140)
seven.setFocusPolicy(QtCore.Qt.NoFocus)
seven.setFixedSize(20,20)
eight = QPushButton('8', self)
eight.move(40,140)
eight.setFocusPolicy(QtCore.Qt.NoFocus)
eight.setFixedSize(20,20)
nine = QPushButton('9', self)
nine.move(70,140)
nine.setFocusPolicy(QtCore.Qt.NoFocus)
nine.setFixedSize(20,20)
zero = QPushButton('0', self)
zero.move(100,140)
zero.setFocusPolicy(QtCore.Qt.NoFocus)
zero.setFixedSize(20,20)
enter = QPushButton("E", self)
enter.move(100,110)
enter.setFixedSize(20,20)
enter.setFocusPolicy(QtCore.Qt.NoFocus)
#click Handles
def handleBackspace():
backh = QtGui.qApp.focusWidget()
if backh is self.line1 or backh is self.line2:
backh.backspace()
def handleZero():
zeroh = QtGui.qApp.focusWidget()
if zeroh is self.line1:
zeroh.setText((self.line1.text()+str('0')))
else:
zeroh.setText(self.line2.text()+str('0'))
def handleOne():
oneh = QtGui.qApp.focusWidget()
if oneh is self.line1:
oneh.setText(self.line1.text()+str('1'))
else:
oneh.setText(self.line2.text()+str('1'))
def handleTwo():
twoh = QtGui.qApp.focusWidget()
if twoh is self.line1:
twoh.setText(self.line1.text()+str('2'))
else:
twoh.setText(self.line2.text()+str('2'))
def handleThree():
threeh = QtGui.qApp.focusWidget()
if threeh is self.line1:
threeh.setText(self.line1.text()+str('3'))
else:
threeh.setText(self.line2.text()+str('3'))
def handleFour():
fourh = QtGui.qApp.focusWidget()
if fourh is self.line1:
fourh.setText(self.line1.text()+str('4'))
else:
fourh.setText(self.line2.text()+str('4'))
def handleFive():
fiveh = QtGui.qApp.focusWidget()
if fiveh is self.line1:
fiveh.setText(self.line1.text()+str('5'))
else:
fiveh.setText(self.line2.text()+str('5'))
def handleSix():
sixh = QtGui.qApp.focusWidget()
if sixh is self.line1:
sixh.setText(self.line1.text()+str('6'))
else:
sixh.setText(self.line2.text()+str('6'))
def handleSeven():
sevenh = QtGui.qApp.focusWidget()
if sevenh is self.line1:
sevenh.setText(self.line1.text()+str('7'))
else:
sevenh.setText(self.line2.text()+str('7'))
def handleEight():
eighth = QtGui.qApp.focusWidget()
if eighth is self.line1:
eighth.setText(self.line1.text()+str('8'))
else:
eighth.setText(self.line2.text()+str('8'))
def handleNine():
nineh = QtGui.qApp.focusWidget()
if nineh is self.line1:
nineh.setText(self.line1.text()+str('9'))
else:
nineh.setText(self.line2.text()+str('9'))
#Click Conditions
self.connect(enter, SIGNAL("clicked()"), self.close)
zero.clicked.connect(handleZero)
nine.clicked.connect(handleNine)
eight.clicked.connect(handleEight)
seven.clicked.connect(handleSeven)
six.clicked.connect(handleSix)
five.clicked.connect(handleFive)
four.clicked.connect(handleFour)
three.clicked.connect(handleThree)
two.clicked.connect(handleTwo)
one.clicked.connect(handleOne)
back.clicked.connect(handleBackspace)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.setWindowTitle("LoginWindow")
window.resize(130, 180)
window.setMaximumSize(130, 180)
window.setMinimumSize(130, 180)
window.show()
sys.exit(app.exec_())
Related
In PyQt, I have a basic program. It consists of 2 combo boxes, 1 line edit and 3 checkboxes. What I want to do is, depending on the item of the first combo box, hide / show specific widgets. However, I keep getting an error: 'ExportDialog' object has no attribute 'exportSetDelimiter_lbl'. I have defined this widget above in initUI, and I run initUIininit`, so I'm not sure why I am getting this error. Here is my code:
from PyQt5 import QtGui, QtCore, QtWidgets
import sys
class ExportDialog(QtWidgets.QMainWindow):
def __init__(self,imagePath):
super(ExportDialog, self).__init__()
self.initUI(imagePath)
#Set The GUI Position And Size
self.setGeometry(500, 500, 600, 450)
#Set The GUI Title
self.setWindowTitle("Export Deck")
#Set The GUI Icon
self.setWindowIcon(QtGui.QIcon('MainFlashcardAppIcon.png'))
def initUI(self, PATH):
#Create The New Deck Label
self.exportFormat_lbl = QtWidgets.QLabel(self)
self.exportFormat_lbl.setText("Export Format: ")
exportFormat_font = QtGui.QFont()
exportFormat_font.setPointSize(8)
self.exportFormat_lbl.setFont(exportFormat_font)
self.exportFormat_lbl.adjustSize()
self.exportFormat_combo = QtWidgets.QComboBox()
self.exportFormat_combo.setMinimumHeight(35)
self.exportFormat_combo.setFixedWidth(380)
self.exportFormat_combo.currentTextChanged.connect(self.on_combobox_changed)
self.exportDeckName_lbl = QtWidgets.QLabel(self)
self.exportDeckName_lbl.setText("Include: ")
self.exportDeckName_lbl.setFont(exportFormat_font)
self.exportDeckName_lbl.adjustSize()
self.exportDeckName_combo = QtWidgets.QComboBox()
self.exportDeckName_combo.setMinimumHeight(35)
self.exportDeckName_combo.setFixedWidth(380)
self.exportFormat_combo.addItem(".TXT")
self.exportFormat_combo.addItem(".CSV")
self.exportFormat_combo.addItem(".DB")
self.exportSetDelimiter_lbl = QtWidgets.QLabel()
self.exportSetDelimiter_lbl.setText("Set Delimiter (Leave blank for standard delimited):")
self.exportSetDelimiter_txt = QtWidgets.QLineEdit()
self.exportSetDelimiter_txt.setMaxLength(1)
self.exportSetDelimiter = QtWidgets.QLineEdit()
vboxExport_setDelimiter = QtWidgets.QVBoxLayout()
vboxExport_setDelimiter.addWidget(self.exportSetDelimiter_lbl)
vboxExport_setDelimiter.addWidget(self.exportSetDelimiter_txt)
self.includeMedia_check = QtWidgets.QCheckBox("Include HTML and Media References")
self.includeTags_check = QtWidgets.QCheckBox("Include Tags")
self.includeAllSQL_check = QtWidgets.QCheckBox("Include All SQL Tables")
self.exportFormat_combo.addItem("B3 Biology")
self.exportFormat_combo.addItem("B2 Biology")
self.exportFormat_combo.addItem("B1 Biology")
self.allComboList = ["B3 Biology", "B2 Biology", "B1 Biology"]
self.exportDeckName_combo.setCurrentIndex(self.allComboList.index(PATH))
#Create Confirm Button
self.confirmButton = QtWidgets.QPushButton(self)
self.confirmButton.setText("OK")
self.confirmButton.clicked.connect(self.createDeck)
#Create Cancel Button
self.cancelButton = QtWidgets.QPushButton(self)
self.cancelButton.setText("Cancel")
self.cancelButton.clicked.connect(self.close)
hboxExportFormat = QtWidgets.QHBoxLayout()
hboxExportFormat.addWidget(self.exportFormat_lbl)
hboxExportFormat.addStretch()
hboxExportFormat.addWidget(self.exportFormat_combo)
hboxExportName = QtWidgets.QHBoxLayout()
hboxExportName.addWidget(self.exportDeckName_lbl)
hboxExportName.addStretch()
hboxExportName.addWidget(self.exportDeckName_combo)
hboxButtonsBottom = QtWidgets.QHBoxLayout()
hboxButtonsBottom.addStretch()
hboxButtonsBottom.addWidget(self.confirmButton)
hboxButtonsBottom.addWidget(self.cancelButton)
#Create The VBoxLayout
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.addLayout(hboxExportFormat)
mainLayout.addLayout(hboxExportName)
mainLayout.addLayout(vboxExport_setDelimiter)
mainLayout.addWidget(self.includeMedia_check)
mainLayout.addWidget(self.includeTags_check)
mainLayout.addWidget(self.includeAllSQL_check)
mainLayout.addStretch()
mainLayout.addLayout(hboxButtonsBottom)
def on_combobox_changed(self, i):
if i == ".TXT":
self.exportSetDelimiter_lbl.show()
self.exportSetDelimiter_txt.show()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.hide()
elif i == ".CSV":
self.exportSetDelimiter_lbl.hide()
self.exportSetDelimiter_txt.hide()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.hide()
elif i == ".DB":
self.exportSetDelimiter_lbl.hide()
self.exportSetDelimiter_txt.hide()
self.includeMedia_check.show()
self.includeTags_check.show()
self.includeAllSQL_check.show()
def createDeck(self):
print("Exported Sucessfully")
self.close()
#Create A Windows
app = QtWidgets.QApplication(sys.argv)
window = ExportDialog("B1 Biology")
window.show()
sys.exit(app.exec_())
This is my first question, so if you need any additional information, I will add it in. Any help would be much appreciated. Thank you!
When a combobox is newly created, it has an invalid current index (-1) and no current text set. As soon as the first item is added, the index is automatically updated to 0 and the current text changes to that of the item.
You've connected to the currentTextChanged signal before adding new items, and since the function currentTextChanged assumes that the whole ui has been already created (including exportSetDelimiter_lbl), you get the attribute error.
While there's no rule for the placing of signal connections, it's usually a good habit to group all connections at the end of the function that creates them, or anyway, ensure that everything required by their connection has already been created.
So, just move the signal connection at the end of initUI and everything will work fine.
Well... No. Because you didn't set a central widget for the main window and tried to set the layout on it (which is not allowed, since a QMainWindow has a private and unaccessible layout).
Add a QWidget, call self.setCentralWidget(someWidget) and create the layout for that widget.
I have an application where upon start up the user is presented with a dialog to chose number of 'objects' required. This then generates necessary objects in the main window using a for loop (i.e. object1, object2, etc.). I want to move this selection into the main window so that this can be changed without the need to restart the application. I have no idea how to approach this as I'm not sure how to dynamically create/destroy once the application is running. Here's an example code that generates tabs in a tab widget with some elements in each tab.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class SelectionWindow(QDialog):
def __init__(self):
QDialog.__init__(self)
self.settings = QSettings('Example', 'Example')
self.numberOfTabs = QSpinBox(value = self.settings.value('numberOfTabs', type=int, defaultValue = 3), minimum = 1)
self.layout = QFormLayout(self)
self.button = QPushButton(text = 'OK', clicked = self.buttonClicked)
self.layout.addRow('Select number of tabs', self.numberOfTabs)
self.layout.addRow(self.button)
def buttonClicked(self):
self.settings.setValue('numberOfTabs', self.numberOfTabs.value())
self.accept()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.settings = QSettings('Example', 'Example')
self.tabs = self.settings.value('numberOfTabs', type = int)
self.tabWidget = QTabWidget()
for i in range(1, self.tabs + 1):
exec(('self.tab{0} = QWidget()').format(i))
exec(("self.tabWidget.addTab(self.tab{0}, 'Tab{0}')").format(i))
exec(('self.lineEdit{0} = QLineEdit()').format(i))
exec(('self.spinBox{0} = QSpinBox()').format(i))
exec(('self.checkBox{0} = QCheckBox()').format(i))
exec(('self.layout{0} = QFormLayout(self.tab{0})').format(i))
exec(("self.layout{0}.addRow('Name', self.lineEdit{0})").format(i))
exec(("self.layout{0}.addRow('Value', self.spinBox{0})").format(i))
exec(("self.layout{0}.addRow('On/Off', self.checkBox{0})").format(i))
self.setCentralWidget(self.tabWidget)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
dialog = SelectionWindow()
dialog.show()
if dialog.exec_() == SelectionWindow.Accepted:
mainwindow = MainWindow()
mainwindow.show()
sys.exit(app.exec_())
First of all, you should never use exec for things like these. Besides the security issues of using exec, it also makes your code less readable (and then much harder to debug) and hard to interact with.
A better (and more "elegant") solution is to use a common function to create tabs and, most importantly, setattr.
Also, you shouldn't use QSettings in this way, as it is mostly intended for cross-session persistent data, not to initialize an interface. For that case, you should just override the exec() method of the dialog and initialize the main window with that value as an argument.
And, even if it was the case (but I suggest you to avoid the above approach anyway), remember that to make settings persistent, at least organizationName and applicationName must be set.
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.settings = QSettings('Example', 'Example')
# this value does not need to be a persistent instance attribute
tabCount = self.settings.value('numberOfTabs', type = int)
# create a main widget for the whole interface
central = QWidget()
mainLayout = QVBoxLayout(central)
tabCountSpin = QSpinBox(minimum=1)
mainLayout.addWidget(tabCountSpin)
tabCountSpin.setValue(tabCount)
tabCountSpin.valueChanged.connect(self.tabCountChanged)
self.tabWidget = QTabWidget()
mainLayout.addWidget(self.tabWidget)
for t in range(tabCount):
self.createTab(t)
self.setCentralWidget(central)
def createTab(self, t):
t += 1
tab = QWidget()
self.tabWidget.addTab(tab, 'Tab{}'.format(t))
layout = QFormLayout(tab)
# create all the widgets
lineEdit = QLineEdit()
spinBox = QSpinBox()
checkBox = QCheckBox()
# add them to the layout
layout.addRow('Name', lineEdit)
layout.addRow('Value', spinBox)
layout.addRow('On/Off', checkBox)
# keeping a "text" reference to the widget is useful, but not for
# everything, as tab can be accessed like this:
# tab = self.tabWidget.widget(index)
# and so its layout:
# tab.layout()
setattr(tab, 'lineEdit{}'.format(t), lineEdit)
setattr(tab, 'spinBox{}'.format(t), spinBox)
setattr(tab, 'checkBox{}'.format(t), checkBox)
def tabCountChanged(self, count):
if count == self.tabWidget.count():
return
elif count < self.tabWidget.count():
while self.tabWidget.count() > count:
# note that I'm not deleting the python reference to each object;
# you should use "del" for both the tab and its children
self.tabWidget.removeTab(count)
else:
for t in range(self.tabWidget.count(), count):
self.createTab(t)
I currently am having an issue with a delete button I created that is linked to a QGraphicsScene class. The button is created in the Window class not the MyView class. I am trying to have the user be able to delete marks that were made on a scene but right now it is only deleting the last ellipse item I create and nothing else. The error that pops up usually says that the other objects you are trying to delete are in a different scene. Also the location of the circle object that wants to be deleted is important. So if the user has the cursor over a particular circle that circle item should delete and nothing else. Here's my code:
import sys
from PyQt4 import QtGui, QtCore
#this sets the scene for drawing and the microscope image
class MyView(QtGui.QGraphicsView):
def __init__(self,window):
QtGui.QGraphicsView.__init__(self)
self.window = window
self.scene = QtGui.QGraphicsScene(self)
self.item = QtGui.QGraphicsRectItem(400, 400, 400, 400)
self.scene.addItem(self.item)
self.setScene(self.scene)
def paintMarkers(self,event):
##self.cursor = QtGui.QCursor()
#self.cursor.setShape(2)
p = self.mapToScene(event.x(),event.y())
self.circleItem = QtGui.QGraphicsEllipseItem(p.x(),p.y(),5,5)
self.scene.addItem(self.circleItem)
self.circleItem.setPen(QtGui.QPen(QtCore.Qt.red, 1.5))
#self.setScene(self.scene)
def deleteMarkers(self):
self.scene.removeItem(self.circleItem)
#print "Hello world"
#def mousePressEvent(self,QMouseEvent):
#self.paintMarkers()
def mousePressEvent(self,event):
if self.window.btnPaintDot.isChecked():
self.paintMarkers(event)
if self.window.btnDeleteMarks.isChecked():
self.deleteMarkers()
return QtGui.QGraphicsView.mousePressEvent(self,event)
class Window(QtGui.QMainWindow):
def __init__(self):
#This initializes the main window or form
super(Window,self).__init__()
self.setGeometry(50,50,1000,1000)
self.setWindowTitle("Pre-Alignment system")
self.view = MyView()
self.setCentralWidget(self.view)
#makes deletemarks button checked when pressed
def paintDeleteMarks(self):
if self.btnDeleteMarks.isChecked():
self.btnPaintDot.setChecked(False)
self.btnPaintPolygon.setChecked(False)
self.btnPaintPolygon.setChecked(False)
self.btnDeleteMarks.setChecked(True)
else:
self.btnDeleteMarks.setChecked(False)
Much thanks please ask questions if my explanation needs more...well explaining.
If you carefully read over your code, you will see that you are deleting the item stored in self.circleItem. The item stored in that variable is always only the last one created (you overwrite the variable each time you create a new item).
You need to modify your code so that it finds items based on the current x-y coordinate of the mouse event. Use QGraphicsScene.itemAt() to find the item at a particular x-y coordinate (remember to correctly transform the coordinates relative to the scene before looking up the item at that location).
Here is the code that fixed the issue thanks to three_pineapples!
import sys
from PyQt4 import QtGui, QtCore
#this sets the scene for drawing and the microscope image
class MyView(QtGui.QGraphicsView):
def __init__(self,window):
QtGui.QGraphicsView.__init__(self)
self.window = window
self.scene = QtGui.QGraphicsScene(self)
self.item = QtGui.QGraphicsRectItem(400, 400, 400, 400)
self.scene.addItem(self.item)
self.setScene(self.scene)
def paintMarkers(self,event):
##self.cursor = QtGui.QCursor()
#self.cursor.setShape(2)
p = self.mapToScene(event.x(),event.y())
if (p.x() > 400 and p.x() < 800) and (p.y() > 400 and p.y() < 800):
self.circleItem = QtGui.QGraphicsEllipseItem(p.x(),p.y(),5,5)
self.scene.addItem(self.circleItem)
self.circleItem.setPen(QtGui.QPen(QtCore.Qt.red, 1.5))
#self.setScene(self.scene)
def deleteMarkers(self,event):
p = self.mapToScene(event.x(),event.y())
if self.scene.itemAt(p.x(),p.y()) != self.item:
self.scene.removeItem(self.scene.itemAt(p.x(),p.y()))
#print "Hello world"
#def mousePressEvent(self,QMouseEvent):
#self.paintMarkers()
def mousePressEvent(self,event):
if self.window.btnPaintDot.isChecked():
self.paintMarkers(event)
if self.window.btnDeleteMarks.isChecked():
self.deleteMarkers(event)
return QtGui.QGraphicsView.mousePressEvent(self,event)
EDIT: There are a number of similar posts on PyQt4 progress bars not updating. They all focus on the issue of threads & where the program actually updates the window. Although helpful, my code was so structured that the replies were not practical. The accepted answer given here is simple, to the point & works.
I am using Python 2.7 and PyQT 4 on a Win 7 x64 machine.
I am trying to clear my window of one widget, an 'Accept' button, see code, and replace it with a progress bar.
Even though I close the 'Accept' button & add the progress bar before the processing loop is entered into. The window is only updated after the loop has finished & the progress bar jumps straight to 100%.
My code,
from PyQt4 import QtCore, QtGui
import sys
import time
class CentralWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CentralWidget, self).__init__(parent)
# set layouts
self.layout = QtGui.QVBoxLayout(self)
# Poly names
self.pNames = QtGui.QLabel("Import file name", self)
self.polyNameInput = QtGui.QLineEdit(self)
# Polytype selection
self.polyTypeName = QtGui.QLabel("Particle type", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("")
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
# Place widgets in layout
self.layout.addWidget(self.pNames)
self.layout.addWidget(self.polyNameInput)
self.layout.addWidget(self.polyTypeName)
self.layout.addWidget(polyType)
self.layout.addStretch()
# Combobox choice
def onActivated(self, text):
if text=="Random polyhedra":
self.randomPolyhedra(text)
if text=="Spheres": # not implementaed yet
self.polyTypeName.setText("Not implemented yet.")
self.polyTypeName.adjustSize()
if text=="Waterman polyhedra": # not implementaed yet
self.polyTypeName.setText("Not implemented yet.")
self.polyTypeName.adjustSize()
# New options for random polyhedra choice
def randomPolyhedra(self, text):
self.polyNumberLbl = QtGui.QLabel("How many: ", self)
self.polyNumber = QtGui.QLineEdit(self)
self.acceptSeed = QtGui.QPushButton('Accept') # Accept button created
self.acceptSeed.clicked.connect(lambda: self.ranPolyGen())
self.layout.addWidget(self.polyNumberLbl)
self.layout.addWidget(self.polyNumber)
self.layout.addWidget(self.acceptSeed) # Accept button in layout
self.randFlag = True
self.polyTypeName.setText(text)
self.polyTypeName.adjustSize()
# Act on option choices for random polyhedra
def ranPolyGen(self):
polyCount = int(self.polyNumber.text())
self.progressBar = QtGui.QProgressBar() # Progress bar created
self.progressBar.setMinimum(1)
self.progressBar.setMaximum(polyCount)
self.acceptSeed.close() # Accept button closed
self.layout.addWidget(self.progressBar) # Add progressbar to layout
for poly in range(1, polyCount+1):
time.sleep(1) # Calls to main polyhedral generating code go here
print poly
self.progressBar.setValue(poly)
self.doneLbl = QtGui.QLabel("Done", self)
self.layout.addWidget(self.doneLbl)
# Creates GUI
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
# Place central widget in layout
self.central_widget = CentralWidget(self)
self.setCentralWidget(self.central_widget)
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
self.central_widget.onActivated(text)
def main():
app = QtGui.QApplication(sys.argv)
poly = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Below is a picture of during loop execution & after completion.
I dont think I have got my head around the addWidget() method. I was under the impression that this would add another widget to the present layout (a vbox layout here) & that the .close() method removed a widget when directed to do so.
What am I missing?
You can add:
from PyQt4.QtGui import QApplication
Then in your for loop:
QApplication.processEvents()
Your app is actually becoming unresponsive, you need to call processEvents() to process the events and redraw the gui. I am not overly familiar with pyqt but I imagine another alternative is using a thread.
I have realized a python simple application, without any animation on it.
Now I want to add a simple animation, triggered by a signal (a button click for example), which on trigger enlarges the width of the windows and shows a new text area with some text in it.
Honestly, I am quite new to python/pyqt4, and I do not know much about the animation framework.
I tried to add this to my class code, for example in a method called clicking on the about menu :) :
self.anim = QPropertyAnimation(self, "size")
self.anim.setDuration(2500)
self.anim.setStartValue(QSize(self.width(), self.height()))
self.anim.setEndValue(QSize(self.width()+100, self.height()))
self.anim.start()
and this enlarge my window as I want.
Unfortunately I have no idea how to insert a new text area, avoiding the widgets already present to fill the new space (actually, when the window enlarge, the widgets use
all the spaces, thus enlarging themselves)
Could someone help me knowing how to add the text area appearance animation?
Any help is appreciated...really...
One way to achieve this is to animate the maximumWidth property on both the window and the text-edit.
The main difficulty is doing it in a way that plays nicely with standard layouts whilst also allowing resizing of the window. Avoiding flicker during the animation is also quite tricky.
The following demo is almost there (the animation is slightly jerky at the beginning and end):
from PyQt4 import QtGui, QtCore
class Window(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
self._offset = 200
self._closed = False
self._maxwidth = self.maximumWidth()
self.widget = QtGui.QWidget(self)
self.listbox = QtGui.QListWidget(self.widget)
self.button = QtGui.QPushButton('Slide', self.widget)
self.button.clicked.connect(self.handleButton)
self.editor = QtGui.QTextEdit(self)
self.editor.setMaximumWidth(self._offset)
vbox = QtGui.QVBoxLayout(self.widget)
vbox.setContentsMargins(0, 0, 0, 0)
vbox.addWidget(self.listbox)
vbox.addWidget(self.button)
layout = QtGui.QHBoxLayout(self)
layout.addWidget(self.widget)
layout.addWidget(self.editor)
layout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize)
self.animator = QtCore.QParallelAnimationGroup(self)
for item in (self, self.editor):
animation = QtCore.QPropertyAnimation(item, 'maximumWidth')
animation.setDuration(800)
animation.setEasingCurve(QtCore.QEasingCurve.OutCubic)
self.animator.addAnimation(animation)
self.animator.finished.connect(self.handleFinished)
def handleButton(self):
for index in range(self.animator.animationCount()):
animation = self.animator.animationAt(index)
width = animation.targetObject().width()
animation.setStartValue(width)
if self._closed:
self.editor.show()
animation.setEndValue(width + self._offset)
else:
animation.setEndValue(width - self._offset)
self._closed = not self._closed
self.widget.setMinimumSize(self.widget.size())
self.layout().setSizeConstraint(QtGui.QLayout.SetFixedSize)
self.animator.start()
def handleFinished(self):
if self._closed:
self.editor.hide()
self.layout().setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize)
self.widget.setMinimumSize(0, 0)
self.setMaximumWidth(self._maxwidth)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.move(500, 300)
window.show()
sys.exit(app.exec_())