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)
Related
I'm trying to have a + button added to a QTabBar. There's a great solution from years ago, with a slight issue that it doesn't work with PySide2. The problem is caused by the tabs auto resizing to fill the sizeHint, which in this case isn't wanted as the extra space is needed. Is there a way I can disable this behaviour?
I've tried QTabBar.setExpanding(False), but according to this answer, the property is mostly ignored:
The bad news is that QTabWidget effectively ignores that property, because it always forces its tabs to be the minimum size (even if you set your own tab-bar).
The difference being in PySide2, it forces the tabs to be the preferred size, where I'd like the old behaviour of minimum size.
Edit: Example with minimal code. The sizeHint width stretches the tab across the full width, whereas in older Qt versions it doesn't do that. I can't really override tabSizeHint since I don't know what the original tab size should be.
import sys
from PySide2 import QtCore, QtWidgets
class TabBar(QtWidgets.QTabBar):
def sizeHint(self):
return QtCore.QSize(100000, super().sizeHint().height())
class Test(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)
tabWidget = QtWidgets.QTabWidget()
tabWidget.setTabBar(TabBar())
layout.addWidget(tabWidget)
tabWidget.addTab(QtWidgets.QWidget(), 'this shouldnt be stretched')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
test = Test()
test.show()
sys.exit(app.exec_())
I think there may be an easy solution to your problem (see below). Where the linked partial solution calculated absolute positioning for the '+' button, the real intent with Qt is always to let the layout engine do it's thing rather than trying to tell it specific sizes and positions. QTabWidget is basically a pre-built amalgamation of layouts and widgets, and sometimes you just have to skip the pre-built and build your own.
example of building a custom TabWidget with extra things across the TabBar:
import sys
from PySide2 import QtWidgets
from random import randint
class TabWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
#layout for entire widget
vbox = QtWidgets.QVBoxLayout(self)
#top bar:
hbox = QtWidgets.QHBoxLayout()
vbox.addLayout(hbox)
self.tab_bar = QtWidgets.QTabBar()
self.tab_bar.setMovable(True)
hbox.addWidget(self.tab_bar)
spacer = QtWidgets.QSpacerItem(0,0,QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
hbox.addSpacerItem(spacer)
add_tab = QtWidgets.QPushButton('+')
hbox.addWidget(add_tab)
#tab content area:
self.widget_stack = QtWidgets.QStackedLayout()
vbox.addLayout(self.widget_stack)
self.widgets = {}
#connect events
add_tab.clicked.connect(self.add_tab)
self.tab_bar.currentChanged.connect(self.currentChanged)
def add_tab(self):
tab_text = 'tab' + str(randint(0,100))
tab_index = self.tab_bar.addTab(tab_text)
widget = QtWidgets.QLabel(tab_text)
self.tab_bar.setTabData(tab_index, widget)
self.widget_stack.addWidget(widget)
self.tab_bar.setCurrentIndex(tab_index)
def currentChanged(self, i):
if i >= 0:
self.widget_stack.setCurrentWidget(self.tab_bar.tabData(i))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
test = TabWidget()
test.show()
sys.exit(app.exec_())
All that said, I think the pre-built QTabWidget.setCornerWidget may be exactly what you're looking for (set a QPushButton to the upper-right widget). The example I wrote should much easier to customize, but also much more effort to re-implement all the same functionality. You will have to re-implement some of the signal logic to create / delete / select / rearrange tabs on your own. I only demonstrated simple implementation, which probably isn't bulletproof to all situations.
Using the code from Aaron as a base to start on, I managed to implement all the functionality required to work with my existing script:
from PySide2 import QtCore, QtWidgets
class TabBar(QtWidgets.QTabBar):
def minimumSizeHint(self):
"""Allow the tab bar to shrink as much as needed."""
minimumSizeHint = super(TabBar, self).minimumSizeHint()
return QtCore.QSize(0, minimumSizeHint.height())
class TabWidgetPlus(QtWidgets.QWidget):
tabOpenRequested = QtCore.Signal()
tabCountChanged = QtCore.Signal(int)
def __init__(self, parent=None):
self._addingTab = False
super(TabWidgetPlus, self).__init__(parent=parent)
# Main layout
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
# Bar layout
self._tabBarLayout = QtWidgets.QHBoxLayout()
self._tabBarLayout.setContentsMargins(0, 0, 0, 0)
self._tabBarLayout.setSpacing(0)
layout.addLayout(self._tabBarLayout)
self._tabBar = TabBar()
self._tabBarLayout.addWidget(self._tabBar)
for method in (
'isMovable', 'setMovable',
'tabsClosable', 'setTabsClosable',
'tabIcon', 'setTabIcon',
'tabText', 'setTabText',
'currentIndex', 'setCurrentIndex',
'currentChanged', 'tabCloseRequested',
):
setattr(self, method, getattr(self._tabBar, method))
self._plusButton = QtWidgets.QPushButton('+')
self._tabBarLayout.addWidget(self._plusButton) # TODO: Find location to insert
self._plusButton.setFixedWidth(20)
self._tabBarLayout.addStretch()
# Content area
self._contentArea = QtWidgets.QStackedLayout()
layout.addLayout(self._contentArea)
# Signals
self.currentChanged.connect(self._currentChanged)
self._plusButton.clicked.connect(self.tabOpenRequested.emit)
# Final setup
self.installEventFilter(self)
#QtCore.Slot(int)
def _currentChanged(self, i):
"""Update the widget."""
if i >= 0 and not self._addingTab:
self._contentArea.setCurrentWidget(self.tabBar().tabData(i))
def eventFilter(self, obj, event):
"""Intercept events until the correct height is set."""
if event.type() == QtCore.QEvent.Show:
self.plusButton().setFixedHeight(self._tabBar.geometry().height())
self.removeEventFilter(self)
return False
def tabBarLayout(self):
return self._tabBarLayout
def tabBar(self):
return self._tabBar
def plusButton(self):
return self._plusButton
def tabAt(self, point):
"""Get the tab at a given point.
This takes any layout margins into account.
"""
offset = self.layout().contentsMargins().top() + self.tabBarLayout().contentsMargins().top()
return self.tabBar().tabAt(point - QtCore.QPoint(0, offset))
def addTab(self, widget, name=''):
"""Add a new tab.
Returns:
Tab index as an int.
"""
self._addingTab = True
tabBar = self.tabBar()
try:
index = tabBar.addTab(name)
tabBar.setTabData(index, widget)
self._contentArea.addWidget(widget)
finally:
self._addingTab = False
return index
def insertTab(self, index, widget, name=''):
"""Inserts a new tab.
If index is out of range, a new tab is appended.
Returns:
Tab index as an int.
"""
self._addingTab = True
tabBar = self.tabBar()
try:
index = tabBar.insertTab(index, name)
tabBar.setTabData(index, widget)
self._contentArea.insertWidget(index, widget)
finally:
self._addingTab = False
return index
def removeTab(self, index):
"""Remove a tab."""
tabBar = self.tabBar()
self._contentArea.removeWidget(tabBar.tabData(index))
tabBar.removeTab(index)
if __name__ == '__main__':
import sys
import random
app = QtWidgets.QApplication(sys.argv)
test = TabWidgetPlus()
test.addTab(QtWidgets.QPushButton(), 'yeah')
test.insertTab(0, QtWidgets.QCheckBox(), 'what')
test.insertTab(1, QtWidgets.QRadioButton(), 'no')
test.removeTab(1)
test.setMovable(True)
test.setTabsClosable(True)
def tabTest():
name = 'Tab ' + str(random.randint(0, 100))
index = test.addTab(QtWidgets.QLabel(name), name)
test.setCurrentIndex(index)
test.tabOpenRequested.connect(tabTest)
test.tabCloseRequested.connect(lambda index: test.removeTab(index))
test.show()
sys.exit(app.exec_())
The one difference is if you're using tabWidget.tabBar().tabAt(point), this is no longer guaranteed to be correct as it doesn't take any margins into account. I set the margins to 0 so this shouldn't be an issue, but I also included those corrections in TabWidgetPlus.tabAt.
I only copied a few methods from QTabBar to QTabWidget as some may need extra testing.
I recently created a program that will create QgraphicsEllipseItems whenever the mouse is clicked. That part works! However, it's not in the exact place where my cursor is. It seems to be slightly higher than where my mouse cursor is. I do have a QGraphicsRectItem created so maybe the two items are clashing with each other and moving off of one another? How can I get these circles to be placed on top of the rectangle item? Here's the code
class MyView(QtGui.QGraphicsView):
def __init__(self):
QtGui.QGraphicsView.__init__(self)
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):
self.cursor = QtGui.QCursor()
self.x = self.cursor.pos().x()
self.y = self.cursor.pos().y()
self.circleItem = QtGui.QGraphicsEllipseItem(self.x,self.y,10,10)
self.scene.addItem(self.circleItem)
self.circleItem.setPen(QtGui.QPen(QtCore.Qt.red, 1.5))
self.setScene(self.scene)
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)
def mousePressEvent(self,QMouseEvent):
self.view.paintMarkers()
Much thanks!
There are two issues with the coordinates you are using to place the QGraphics...Items. The first is that the coordinates from QCursor are global screen coordinates, so you need to use self.mapFromGlobal() to convert them to coordinates relative to the QGraphicsView.
Secondly, you actually want the coordinates relative to the current QGraphicsScene, as this is where you are drawing the item. This is because the scene can be offset from the view (for example panning around a scene that is bigger than a view). To do this, you use self.mapToScene() on the coordinates relative to the QGraphicsView.
I would point out that typically you would draw something on the QGraphicsScene in response to some sort of mouse event in the QGraphicsView, which requires reimplementing things like QGraphicsView.mouseMoveEvent or QGraphicsView.mousePressEvent. These event handlers are passed a QEvent which contains the mouse coordinates relative to the view, and so you don't need to do the global coordinates transformation I mentioned in the first paragraph in these cases.
Update
I've just seen your other question, and now understand some of the issue a bit better. You shouldn't be overriding the mouse event in the main window. Instead override it in the view. For example:
class MyView(QtGui.QGraphicsView):
def __init__(self):
QtGui.QGraphicsView.__init__(self)
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):
# event position is in coordinates relative to the view
# so convert them to scene coordinates
p = self.mapToScene(event.x(), event.y())
self.circleItem = QtGui.QGraphicsEllipseItem(0,0,10,10)
self.circleItem.setPos(p.x()-self.circleItem.boundingRect().width()/2.0,
p.y()-self.circleItem.boundingRect().height()/2.0)
self.scene.addItem(self.circleItem)
self.circleItem.setPen(QtGui.QPen(QtCore.Qt.red, 1.5))
# self.setScene(self.scene) # <-- this line should not be needed here
# Note, I've renamed the second argument `event`. Otherwise you locally override the QMouseEvent class
def mousePressEvent(self, event):
self.paintMarkers(event)
# you may want to preserve the default mouse press behaviour,
# in which case call the following
return QGraphicsView.mousePressEvent(self, event)
Here we have not needed to use QWidget.mapFromGlobal() (what I covered in the first paragraph) because we use a mouse event from the QGraphicsView which returns coordinates relative to that widget only.
Update 2
Note: I've updated how the item is created/placed in the above code based on this answer.
I am currently having an issue with finding the distance between two items in a QGraphicsScene. I have the distance formula already down but the issue is that when I select two points or items in the graphicsScene it says they are at the same point and since they are at the same point the distance always comes out zero. To select multiple items I use ctrl + click to get two points selected at once and I know the two points are at different areas in the scene. Here is the code:
import sys
from PyQt4 import QtGui, QtCore
import math
#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.circleItem.setFlag(QtGui.QGraphicsItem.ItemIsSelectable,True)
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)
if self.window.btnPaintLength.isChecked():
self.distanceFormula(self.scene.selectedItems())
return QtGui.QGraphicsView.mousePressEvent(self,event)
def distanceFormula(self,list):
if len(list) == 2:
"""for i in list:
p = self.mapToScene(i.x(),i.y())
print p.x()
print p.y()"""
p = self.mapToScene(list[0].x(),list[0].y())
q = self.mapToScene(list[1].x(),list[1].y())
print p.x()
print p.y()
print q.x()
print q.y()
print list[0]
print list[1]
print len(list)
length = math.sqrt((p.x() - q.x())**2 + (p.y() - q.y())**2)
print length
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)
self.setCentralWidget(self.view)
self.btnPaintLength = QtGui.QPushButton("Len",self)
self.btnPaintLength.clicked.connect(self.paintLengthOnImage)
self.btnPaintLength.setCheckable(True)
self.btnPaintLength.resize(30,30)
self.btnPaintLength.move(330,50)
#this creates the paint button allowing user to draw lines or anything on the image from the microscope
self.btnPaintDot = QtGui.QPushButton("*",self)
self.btnPaintDot.setCheckable(True)
self.btnPaintDot.clicked.connect(self.paintDotOnImage)
self.btnPaintDot.resize(30,30)
self.btnPaintDot.move(300,50)
#creates the delete marker button
self.btnDeleteMarks = QtGui.QPushButton("del",self)
self.btnDeleteMarks.clicked.connect(self.paintDeleteMarks)
self.btnDeleteMarks.setCheckable(True)
self.btnDeleteMarks.resize(30,30)
self.btnDeleteMarks.move(390,50)
def paintLengthOnImage(self):
"""create length on image"""
if self.btnPaintLength.isChecked():
self.btnPaintDot.setChecked(False)
self.btnPaintPolygon.setChecked(False)
self.btnDeleteMarks.setChecked(False)
self.view.distanceFormula(self.view.scene.selectedItems())
else:
self.btnPaintLength.setChecked(False)
def paintDotOnImage(self):
"""create a dot paint button"""
if self.btnPaintDot.isChecked():
self.btnPaintDot.setChecked(True)
self.btnPaintPolygon.setChecked(False)
self.btnPaintLength.setChecked(False)
self.btnDeleteMarks.setChecked(False)
else:
self.btnPaintDot.setChecked(False)
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)
def run():
app = QtGui.QApplication(sys.argv)
GUI = Window()
GUI.show()
sys.exit(app.exec_())
run()
The print statements were just there for my purpose of finding where the issue was. Much thanks!
It looks like this happens because of the way the items were created. Items are always created at (0,0). When you specify the (x,y,width,height) when you construct the item, you actually create an items which extends from (0,0) to (x+width, y+height). You can verify this by looking at the size of the items boundingRect().
Instead, you should construct the object at (0,0) and move it to the correct location. With you current code, this means doing the following when you create the item:
self.circleItem = QtGui.QGraphicsEllipseItem(0,0,5,5)
self.circleItem.setPos(p.x()-self.circleItem.boundingRect().width()/2.0,p.y()-self.circleItem.boundingRect().height()/2.0)
Note, here I offset the position by half of the width/height so that the items is centred on the mouse click. You may or may not want this.
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_())
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_())