I'm having trouble getting a custom slot to work after a radio button in my QGroupBox is clicked. Here's the relevant part of the code (PySide2)
class ChooseTarget(QtWidgets.QGroupBox):
def __init__(self, title=None):
super().__init__(title)
with open(definitions.JSON_PATH) as j_file:
names = json.load(j_file)
self.radios = [QtWidgets.QRadioButton(names['names'][i]) for i in range(11)]
self.radios[names['names'].index(names["lastTarget"])].setChecked(True)
self.layout = QtWidgets.QGridLayout()
self.button = QtWidgets.QPushButton("Ok")
self.styleButton()
self.addToLayout()
def addToLayout(self):
for i in range(11):
#adding radio buttons to grid layout
if i < 4:
self.layout.addWidget(self.radios[i],0,i)
elif i >= 4 and i < 8:
self.layout.addWidget(self.radios[i],1, i - 4)
else:
self.layout.addWidget(self.radios[i],2, i - 8)
self.radios[i].clicked.connect(self.handleClick)
self.layout.addWidget(self.button,2,3)
handleclick for now is just a method with a print, and I verified that it never runs when the radio buttons are clicked. I've tried using the #Slot decorator as well, or the alternative way of connecting sender and receiver with the SIGNAL and SLOT macros.
Edit:
The MainWidget class is the one I show in the main.
class MainWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.mTableOfOils)
self.layout.addWidget(self.text)
self.mRadioButtons = ChooseTarget("Choose the target")
self.layout.addLayout(self.mRadioButtons.layout)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = MainWidget()
widget.resize(800, 600)
widget.show()
sys.exit(app.exec_())
In the MainWidget class I only added the layout of my QGroupBox but didn't add the radio buttons widget to the main layout, now it works as intended
Related
The following running code gives a window with a button on it . The tooltip displays when the mouse enters the button.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtTest import QTest
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.button = QPushButton("MyButton")
global i
i = 0
self.button.setToolTip(str(i) + " seconds has passed since you move your mouse onto MyButton")
self.button.leaveEvent = self.clear()
self.layout.addWidget(self.button)
timer = QTimer(self)
timer.timeout.connect(self.start_counting)
timer.start(1000)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
def clear(self):
global i
i = 0
def start_counting(self):
if self.button.underMouse() == True:
global i
i = i + 1
self.button.setToolTip(str(i) + " seconds has passed since you move your mouse onto MyButton")
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit( app.exec_() )
My goal is to count the number of seconds the mouse is inside the button and live display it using the tooltip. More precisely, I need to make sure all of the following is happening:
Every time the mouse enters the button, the count starts at 0 seconds and the tooltip shows up.
While the mouse stays inside the button (stationary or moving), the tooltip stays shown with the number of seconds (the text displayed inside the tooltip) updated over time.
When the mouse leaves the button, the count is cleared to zero.
As seen in the code, I have attempted to use underMouse to achieve my goals. My attempt is a partial success as the tooltip does update itself when the mouse moves inside the button. However, the tooltip does not update itself when the mouse stays stationary inside the button. Also, the count does not seem to be cleared when the mouse moves outside of the button .
What am I missing ?
One solution is to use an event-filter to monitor the enter and leave events, and also use an elapsed-timer to get an accurate measure of how long the mouse has been over the target widget. Below is a basic demo based on your example that implements the above. It also tries to match the normal behaviour of tooltips, but if necessary you can easily adjust the code to suit your own needs:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.button = QPushButton("MyButton")
self.layout.addWidget(self.button)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
self.position = QPoint()
self.counter = QElapsedTimer()
self.timer = QTimer()
self.timer.timeout.connect(self.start_counting)
self.button.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QEvent.Enter and source is self.button:
self.counter.start()
self.timer.start(1000)
QTimer.singleShot(500, self.start_counting)
elif event.type() == QEvent.Leave and source is self.button:
self.timer.stop()
QToolTip.hideText()
return super().eventFilter(source, event)
def start_counting(self):
if self.button.underMouse():
if not QToolTip.isVisible():
self.position = QCursor.pos()
count = int(self.counter.elapsed() / 1000)
QToolTip.showText(self.position, (
f'{count} seconds have passed since '
'you moved your mouse onto MyButton'
))
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit( app.exec_() )
I am new to PyQt perhaps thats why facing this issue. I am trying to make a component inserter for excel sheets. For this purpose I am using QT for interface and using Qlabels within parent class of QMainWindow. On the basis of item selected from Qlist Widget, few Qlabels have to change on mainwindow dynamically Component inserter
As can be seen in above picture, the labels like WCAP-; Part Number and all below needs to change dynamically when the selected items change(when select button is clicked). But what happening is if I choose a different item from list, the previous Label stays and the new label is overlapping it as can be seen from picture below showing overlapping of labels
The code below shows that whenever button "Select" is pressed", label2 (Qlabel2) is formed, how can i delete the previous label whenever select button is pressed so that new Label dynamically replaces the old label.
Thanks a lot in advance.
def Display(self):
self.close()
label1 = QtGui.QLabel("Select the sheet",self)
label1.move(0,15)
self.listwidget = QtGui.QListWidget(self)
self.listwidget.move(0,40)
self.listwidget.resize(150,150)
for i in range(len(self.sheetnames)):
self.listwidget.addItem("%s"%self.sheetnames[i])
btn = QtGui.QPushButton('Select',self)
btn.resize(50,50)
btn.move(170,40)
btn.clicked.connect(self.Selected)
self.show()
def Selected(self):
self.close()
selecteditem = self.listwidget.currentItem().text()
self.sheetindex = self.sheetnames.index(selecteditem)
print self.sheetindex
aa = self.loadsheet.sheet_by_name(selecteditem)
global label2
label2 = QtGui.QLabel("",self)
label2.setText(selecteditem)
label2.move(0,190)
self.show()
self.InputParameters(aa)
You see a new QLabel because you create a new one every time you call Selected. I would initiate the UI at the creation of the widget (in the __init__ method):
def __init__(self):
self.label2 = QtGui.QLabel("",self)
And only update the text of the Qlabel when Selected is executed:
def Selected(self):
self.label2.setText(selecteditem)
About reinitializing all labels with an unknown number of labels and removing the old ones, you might want to look at QLabel.setParent(None). I wrote you a little example:
from PyQt4 import QtGui, QtCore
import sys
class test(QtGui.QWidget):
def __init__(self,parent=None):
self.widget=QtGui.QWidget.__init__(self, parent)
# Button to add labels
self.btnAdd = QtGui.QPushButton('Add')
self.btnAdd.connect(self.btnAdd, QtCore.SIGNAL('clicked()'),self.btnAddPressed)
# Button to remove labels
self.btnRemove = QtGui.QPushButton('Remove')
self.btnRemove.connect(self.btnRemove, QtCore.SIGNAL('clicked()'), self.btnRemovePressed)
# List to keep track of labels
self.labels=[]
# Layout
self.hbox = QtGui.QHBoxLayout()
self.hbox.addWidget(self.btnAdd)
self.hbox.addWidget(self.btnRemove)
self.setLayout(self.hbox)
self.show()
def btnAddPressed(self):
"""Adds a new label."""
self.labels.append(QtGui.QLabel("lbl"+str(len(self.labels)+1), self))
self.hbox.addWidget(self.labels[-1])
def btnRemovePressed(self):
"""Removes last label."""
self.labels[-1].setParent(None)
self.labels.pop(-1)
def main():
#Creating application
app = QtGui.QApplication(sys.argv)
main_win = test()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I am new to PyQt5... Simple question here.
I am using PyQt5 to build a simple application. This application has a Main Window containing a QTabWidget with 3 tabs. Once the application starts, all tab pages are empty and get filled later on. When tab pages are empty, I would still like them to appear as blank pages and extend up to the Main Window edges.
I've been trying to achieve this in two ways: using a layout and using the setGeometry function. Yet the tab pages never extend vertically very far, and horizontally they never go beyond the last tab. See code below.
import sys
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Window With Tabs")
self.setGeometry(50,50,400,400)
oTabWidget = QTabWidget(self)
oPage1 = QWidget()
oLabel1 = QLabel("Hello",self)
oVBox1 = QVBoxLayout()
oVBox1.addWidget(oLabel1)
oPage1.setLayout(oVBox1)
oPage2 = QWidget()
oPage2.setGeometry(0,0,400,400)
oPage3 = QWidget()
oPage3.setGeometry(0,0,400,400)
oTabWidget.addTab(oPage1,"Page1")
oTabWidget.addTab(oPage2,"Page2")
oTabWidget.addTab(oPage3,"Page3")
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
oMainwindow = MainWindow()
sys.exit(app.exec_())
Any idea how to modify the code so the empty pages will extend up to the edges of Main Window ?
Set a layout on the main widget:
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Window With Tabs")
self.setGeometry(50,50,400,400)
layout = QVBoxLayout(self)
oTabWidget = QTabWidget(self)
layout.addWidget(oTabWidget)
The setGeometry calls on the other widgets are redundant.
import sys
from PyQt5.QtWidgets import *
class MainWindow(QWidget):
# window object
def __init__(self):
super().__init__()
self.initGUI() # call custom code
def initGUI(self):
self.setWindowTitle("Window With Tabs") # window...
self.setGeometry(50,50,400,400) #...properties
TabW=self.createTabs() # a custom-tab object
layout = QVBoxLayout(self) # main window layout
layout.addWidget(TabW) #populate layout with Tab object
self.show() # display window
def createTabs(self): # create and return Tab object
oPage1 = QWidget() # tabs...
oPage2 = QWidget()
oPage3 = QWidget()
oTabWidget = QTabWidget() # Tabobject
oTabWidget.addTab(oPage1,"Page1") # populate tab object...
oTabWidget.addTab(oPage2,"Page2")
oTabWidget.addTab(oPage3,"Page3")
return oTabWidget # return tab object
if __name__ == "__main__": # Rest is History!
app = QApplication(sys.argv)
oMainwindow = MainWindow()
sys.exit(app.exec_())
I have a groupbox with some radiobuttons. How do I get to know which one which is checked.
Another way is to use button groups. For example:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MoodExample(QGroupBox):
def __init__(self):
super(MoodExample, self).__init__()
# Create an array of radio buttons
moods = [QRadioButton("Happy"), QRadioButton("Sad"), QRadioButton("Angry")]
# Set a radio button to be checked by default
moods[0].setChecked(True)
# Radio buttons usually are in a vertical layout
button_layout = QVBoxLayout()
# Create a button group for radio buttons
self.mood_button_group = QButtonGroup()
for i in xrange(len(moods)):
# Add each radio button to the button layout
button_layout.addWidget(moods[i])
# Add each radio button to the button group & give it an ID of i
self.mood_button_group.addButton(moods[i], i)
# Connect each radio button to a method to run when it's clicked
self.connect(moods[i], SIGNAL("clicked()"), self.radio_button_clicked)
# Set the layout of the group box to the button layout
self.setLayout(button_layout)
#Print out the ID & text of the checked radio button
def radio_button_clicked(self):
print(self.mood_button_group.checkedId())
print(self.mood_button_group.checkedButton().text())
app = QApplication(sys.argv)
mood_example = MoodExample()
mood_example.show()
sys.exit(app.exec_())
I found more information at:
http://codeprogress.com/python/libraries/pyqt/showPyQTExample.php?index=387&key=QButtonGroupClick
http://www.pythonschool.net/pyqt/radio-button-widget/
you will need to iterate through all the radio buttons in the groupbox and check for the property isChecked() of each radiobox.
eg:
radio1 = QtGui.QRadioButton("button 1")
radio2 = QtGui.QRadioButton("button 2")
radio3 = QtGui.QRadioButton("button 3")
for i in range(1,4):
buttonname = "radio" + str(i)
if buttonname.isChecked():
print buttonname + "is Checked"
for reference, check http://pyqt.sourceforge.net/Docs/PyQt4/qradiobutton.html
I managed to work around this problem by using a combination of index and loop.
indexOfChecked = [self.ButtonGroup.buttons()[x].isChecked() for x in range(len(self.ButtonGroup.buttons()))].index(True)
def izle(self):
radios=["radio1","radio2","radio3","radio4"]
for i in range(0,4):
selected_radio = self.ui.findChild(QtGui.QRadioButton, self.radios[i])
if selected_radio.isChecked():
print selected_radio.objectName() + "is Checked"
you can get all chaild objects of class from desired parent. something like
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.show()
print(self.getCheckedRbName(self.gbRadioButtonsGroup))
def getCheckedRbName(self, rbParent: QWidget) -> str:
for rb in rbParent.findChildren(QRadioButton):
if rb.isChecked():
return rb.objectName()
gives you
#> 'rbThirdOption' is checked
I am making a simple text editor in wxpython. I would like it to be able to edit code such as python, and as such I would like to have it highlight the text in a similar manner to IDLE or Notepad++. I know how I would highlight it, but I would like the best way of running it. I don't know if it is possible but what I would really like is to run whenever a key is pressed, and not on a loop checking if it is pressed, so as to save on processing.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,600))
style = wx.TE_MULTILINE|wx.BORDER_SUNKEN|wx.TE_RICH2
self.status_area = wx.TextCtrl(self, -1,
pos=(10, 270),style=style,
size=(380,150))
self.status_area.AppendText("Type in your wonderfull code here.")
fg = wx.Colour(200,80,100)
at = wx.TextAttr(fg)
self.status_area.SetStyle(3, 5, at)
self.CreateStatusBar() # A Statusbar in the bottom of the window
# Setting up the menu.
filemenu= wx.Menu()
filemenu.Append(wx.ID_ABOUT, "&About","Use to edit python code")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_EXIT,"&Exit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, "Python Coder")
app.MainLoop()
If a loop is needed what would be the best way to make it loop, with a while loop, or a
def Loop():
<code>
Loop()
My new code with the added bind:
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,600))
style = wx.TE_MULTILINE|wx.BORDER_SUNKEN|wx.TE_RICH2
self.status_area = wx.TextCtrl(self, -1,
pos=(10, 270),style=style,
size=(380,150))
#settup the syntax highlighting to run on a key press
self.Bind(wx.EVT_CHAR, self.onKeyPress, self.status_area)
self.status_area.AppendText("Type in your wonderfull code here.")
fg = wx.Colour(200,80,100)
at = wx.TextAttr(fg)
self.status_area.SetStyle(3, 5, at)
self.CreateStatusBar() # A Statusbar in the bottom of the window
# Setting up the menu.
filemenu= wx.Menu()
filemenu.Append(wx.ID_ABOUT, "&About","Use to edit python code")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_EXIT,"&Exit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
self.Show(True)
def onKeyPress (self, event):
print "KEY PRESSED"
kc = event.GetKeyCode()
if kc == WXK_SPACE or kc == WXK_RETURN:
Line = self.status_area.GetValue()
print Line
app = wx.App(False)
frame = MainWindow(None, "Python Coder")
app.MainLoop()
In your MainWindow __init__ function add this
self.Bind(wx.EVT_CHAR, self.onKeyPress, self.status_area)
then define onKeyPress in MainWindow
def onKeyPress (self, event):
kc = event.GetKeyCode()
if kc == WXK_SPACE or kc == WXK_RETURN:
#Run your highlighting code here
Come to think of it, this might not be the most efficient way of doing code highlighting. Let me look this up. But in the meantime you can try this.
Edit:
Take a look at this - StyledTextCtrl . I think its more along the lines of what you need.
I solved this when I faced the same issue by creating a custom event.
First, I created a subclass of the TextCtrl, so I had a place in code to raise/post the custom event from:
import wx.lib.newevent
(OnChangeEvent, EVT_VALUE_CHANGED) = wx.lib.newevent.NewEvent()
class TextBox(wx.TextCtrl):
old_value = u''
def __init__(self,*args,**kwargs):
wx.TextCtrl.__init__(self,*args,**kwargs)
self.Bind(wx.EVT_SET_FOCUS, self.gotFocus) # used to set old value
self.Bind(wx.EVT_KILL_FOCUS, self.lostFocus) # used to get new value
def gotFocus(self, evt):
evt.Skip()
self.old_value = self.GetValue()
def lostFocus(self, evt):
evt.Skip()
if self.GetValue() != self.old_value:
evt = OnChangeEvent(oldValue=self.old_value, newValue=self.GetValue())
wx.PostEvent(self, evt)
Now, in my frame's code, here is a snippet of me Binding the event, and using it.
summ_text_ctrl = TextBox(self, -1, size=(400, -1))
summ_text_ctrl.Bind(EVT_VALUE_CHANGED, self.onTextChanged)
def OnTextChanged(self, evt):
evt.Skip()
print('old Value: %s' % evt.oldValue )
print('new Value: %s' % evt.newValue )