So i am using Pyqt for creating GUI program using Python programming language. And i am trying to get the value of Qslider when clicking it, but i don't understand how...
self.slide.mouseDoubleClickEvent= lambda event: self.slideclicked()
This is how i declare the method when the slide is clicked, and this is the method :
def slideclicked(self):
print(self.slide.value())
I am hoping to get the value where the mouse clicked, but instead i am just getting current value of the Qslider.
A possible solution is to overwrite the mouseDoubleClickEvent method and create a signal that sends that information:
class Slider(QSlider):
pointClicked = pyqtSignal(QPoint)
def mouseDoubleClickEvent(self, event):
self.pointClicked.emit(event.pos())
class Widget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.setLayout(QVBoxLayout())
self.slider = Slider()
self.layout().addWidget(self.slider)
self.slider.pointClicked.connect(lambda p: print(p.x(), p.y()))
If you can not overwrite that method you could use eventFilter:
class ClickedHelper(QObject):
pointClicked = pyqtSignal(QPoint)
def __init__(self, widget, *args, **kwargs):
QObject.__init__(self, parent=widget)
self.obj = widget
self.obj.installEventFilter(self)
def eventFilter(self, obj, event):
if obj == self.obj and event.type() == QEvent.MouseButtonDblClick:
self.pointClicked.emit(event.pos())
return QObject.eventFilter(self, obj, event)
class Widget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.setLayout(QVBoxLayout())
self.slider = QSlider()
self.layout().addWidget(self.slider)
helper = ClickedHelper(self.slider)
helper.pointClicked.connect(lambda p: print(p.x(), p.y()))
Related
In my plugin there is a main dialog, showing list data, and a button that opens another dialog to edit the data in. When you finish the editing the data is saved to a file to be accessed later but a refresh needs to be triggered in the main dialog.
Main Dialog has a refresh function but how can I trigger it from the another "subDialog"?
Main Class
class MainDialog(QDialog):
def __init__(self, iface, parent=None, flags=Qt.WindowFlags()):
QDialog.__init__(self, parent, flags)
uic.loadUi(UI_PATH, self)
self.AnotherClass_instance = AnotherClass(self.iface)
#code to open another dialog when edit is pressed
self.refreshDatabaseData()
def opne_the_other_dialog(self):
self.AnotherClass_instance.execDialog()
def refreshDatabaseData(self):
#code to read txt file and change list view in UI
class MainClass(object):
def __init__(self, iface):
self.act.triggered.connect(self.execDialog)
def initGui(self, menu=None):
if menu is not None:
menu.addAction(self.act)
else:
self.iface.addToolBarIcon(self.act)
def execDialog(self):
self.dialog = MainDialog(self.iface, self.iface.mainWindow())
self.dialog.show()
def quitDialog(self):
self.dialog = None
self.act.setEnabled(True)
self.cancel = False
def execTool(self):
#Do something related to plugin
self.quitDialog()
The other class:
class AnotherClassDialog(QDialog):
def __init__(self, iface, parent=None, flags=Qt.WindowFlags()):
QDialog.__init__(self, parent, flags)
uic.loadUi(UI_PATH, self)
self.iface = iface
class AnotherClass(object):
def __init__(self, iface):
self.iface = iface
self.dialog = None
self.cancel = False
self.act.triggered.connect(self.execDialog)
#connect here the buttons to functions, e.g. "OK"-button to execTool
def execDialog(self):
self.dialog = AnotherClassDialog(self.iface, self.iface.mainWindow())
self.dialog.show()
def scheduleAbort(self):
self.cancel = True
def refreshMainPlugin(self):
#need to execute this correctly
self.refreshDatabaseData()
Edit:
I tried passing the list view in self.AnotherClass_instance.execDialog(here), It works but then I can't pass it from AnotherClass to AnotherClassDialog (it changes from Class Object to QMainWindow).
I'd like to modify QTreeWidget to make the selected cell editable when the enter key is hit, but keep the selection to full rows.
I've done a hacky implementation of figuring out where the last click was and saving the value, then sending those values to my edit_item function on the key press (also used for the itemDoubleClicked signal). It's not great though and I'm wondering if there's a much easier way to do it.
For the record, clicking on an item still selects the whole row. It's probably hidden behaviour by default, but in Maya there's a visible selection thing of the last cell that was moved over while the mouse button was held. If I could somehow get access to that, I could also add in behaviour to control it with the arrow keys.
This is an example of the selected cell:
This is my code so far:
class QTreeWidget(QtWidgets.QTreeWidget):
returnPressed = QtCore.Signal(QTreeWidget, int)
def __init__(self, *args, **kwargs):
QtWidgets.QTreeWidget.__init__(self, *args, **kwargs)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Return:
self.returnPressed.emit(self._selected_item, self._selected_column)
else:
QtWidgets.QTreeWidget.keyPressEvent(self, event)
def _mouse_pos_calculate(self, x_pos):
"""Find the currently selected column."""
try:
item = self.selectedItems()[0]
except IndexError:
item = None
header = self.header()
total_width = 0
for i in range(self.columnCount()):
total_width += header.sectionSize(i)
if total_width > x_pos:
return (item, i)
def mousePressEvent(self, event):
QtWidgets.QTreeWidget.mousePressEvent(self, event)
self._selected_item, self._selected_column = self._mouse_pos_calculate(event.pos().x())
def mouseReleaseEvent(self, event):
QtWidgets.QTreeWidget.mouseReleaseEvent(self, event)
self._selected_item, self._selected_column = self._mouse_pos_calculate(event.pos().x())
Edit: Improved function thanks to eyllanesc
class QTreeWidget(QtWidgets.QTreeWidget):
"""Add ability to edit cells when pressing return."""
itemEdit = QtCore.Signal(QtWidgets.QTreeWidgetItem, int)
def __init__(self, *args, **kwargs):
QtWidgets.QTreeWidget.__init__(self, *args, **kwargs)
self._last_item = None
self._last_column = 0
self.itemDoubleClicked.connect(self._edit_item_intercept)
def _edit_item_intercept(self, item=None, column=None):
if item is None:
item = self._last_item
if column is None:
column = self._last_column
self.itemEdit.emit(item, column)
def _store_last_cell(self, pos):
selected_item = self.itemAt(pos)
if selected_item is None:
return
self._last_item = selected_item
self._last_column = self.header().logicalIndexAt(pos.x())
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Return:
return self._edit_item_intercept()
QtWidgets.QTreeWidget.keyPressEvent(self, event)
def mouseMoveEvent(self, event):
QtWidgets.QTreeWidget.mouseMoveEvent(self, event)
self._store_last_cell(event.pos())
You are doing a lot of calculation unnecessarily, in the next part I show a cleaner solution:
from PySide2 import QtCore, QtGui, QtWidgets
class QTreeWidget(QtWidgets.QTreeWidget):
def __init__(self, *args, **kwargs):
super(TreeWidget, self).__init__(*args, **kwargs)
self.special_item = None
self.special_col = 0
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Return:
self.editItem(self.special_item, self.special_col)
QtWidgets.QTreeWidget.keyPressEvent(self, event)
def editEnable(self, pos):
press_item = self.itemAt(pos)
if press_item is None:
return
if press_item is self.selectedItems()[0]:
col = self.header().logicalIndexAt(pos.x())
self.special_item = press_item
self.special_col = col
def mousePressEvent(self, event):
QtWidgets.QTreeWidget.mousePressEvent(self, event)
self.editEnable(event.pos())
I am working on set a click() event to QLineEdit, I already successfully did it. But I want to go back to Mainwindow when the QLine Edit is clicked because I need the data in Mainwindow to further process the data. But I failed to let it go back, neither nor to cite the Mainwindow as parent, I hope someone can point it out. Thank you so much.
MainWindow
{
...
self.tc = MyLineEdit(self.field[con.ConfigFields.VALUE])#self.tc = wx.TextCtrl(self.parent, -1, str(field[con.ConfigFields.VALUE]), pos=(x+220, y-3), size=(200, -1))
...
}
class MyLineEdit(QtGui.QLineEdit):
def __init__(self, parent=MainWindow):
super(MyLineEdit, self).__init__(parent)
#super(CustomQLineEidt, self).__init__()
def mousePressEvent(self, e):
self.mouseseleted()
def mouseseleted(self):
print "here"
MainWindow.mousePressEvent
I use the following to connect any method as the callback for a click event:
class ClickableLineEdit(QLineEdit):
clicked = pyqtSignal() # signal when the text entry is left clicked
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton: self.clicked.emit()
else: super().mousePressEvent(event)
To use:
textbox = ClickableLineEdit('Default text')
textbox.clicked.connect(someMethod)
Specifically for the op:
self.tc = ClickableLineEdit(self.field[con.ConfigFields.VALUE])
self.tc.clicked.connect(self.mouseseleted)
Just simply call the MainWindow mousePressEvent and give it the event variable the line edit received
class MyLineEdit(QtGui.QLineEdit):
def __init__(self, parent):
super(MyLineEdit, self).__init__(parent)
self.parentWindow = parent
def mousePressEvent(self, event):
print 'forwarding to the main window'
self.parentWindow.mousePressEvent(event)
Or you can connect a signal from the line edit
class MyLineEdit(QtGui.QLineEdit):
mousePressed = QtCore.pyqtProperty(QtGui.QMouseEvent)
def __init__(self, value):
super(MyLineEdit, self).__init__(value)
def mousePressEvent(self, event):
print 'forwarding to the main window'
self.mousePressed.emit(event)
Then just connect the signal in your main window where you created it
self.tc = MyLineEdit(self.field[con.ConfigFields.VALUE])#self.tc = wx.TextCtrl(self.parent, -1, str(field[con.ConfigFields.VALUE]), pos=(x+220, y-3), size=(200, -1))
self.tc.mousePressed[QtGui.QMouseEvent].connect(self.mousePressEvent)
This is what I used to do onClick for QLineEdits
class MyLineEdit(QtGui.QLineEdit):
def focusInEvent(self, e):
try:
self.CallBack(*self.CallBackArgs)
except AttributeError:
pass
super().focusInEvent(e)
def SetCallBack(self, callBack):
self.CallBack = callBack
self.IsCallBack = True
self.CallBackArgs = []
def SetCallBackArgs(self, args):
self.CallBackArgs = args
and in my MainGUI:
class MainGUI(..):
def __init__(...):
....
self.input = MyLineEdit()
self.input.SetCallBack(self.Test)
self.input.SetCallBackArgs(['value', 'test'])
...
def Test(self, value, test):
print('in Test', value, test)
I am trying to create tqo line edit and when i click on the line edit box i should be able to clear the current text.
I tried the below code but no success ,
Can someone point out what is wrong here?
OPTIONS = ['Enter IP Address','Number of iteration']
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, 'Details', parent=parent)
self.options = {}
for option in OptionBox.OPTIONS:
self.options[option] = (QtGui.QLineEdit(option))
self.connect(self.options[option], QtCore.SIGNAL("clicked()"), self.clicked)
self._gridOptions()
def clicked(self):
QLineEdit.clear()
You need to use an event filter on the QLineEdit to catch the click event in it (https://qt-project.org/doc/qt-5.1/qtcore/qobject.html#eventFilter). Here is an example on how the code should look like:
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, 'Details', parent=parent)
self.options = {}
for option in OptionBox.OPTIONS:
self.options[option] = QtGui.QLineEdit(option)
self.options[option].installEventFilter(self)
self._gridOptions()
def eventFilter(self, object, event):
if (object in self.options.values()) and (event.type() == QtCore.QEvent.MouseButtonPress):
object.clear()
return False # lets the event continue to the edit
return False
Edit: from what I understand, you just want a default text to appear in the QLineEdit which describe their role. This is a good opportunity to use the placeholderText. Here is the code modified to use it (there is no more need of the eventFilter method):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, 'Details', parent=parent)
self.options = {}
for option in OptionBox.OPTIONS:
self.options[option] = QtGui.QLineEdit()
self.options[option].setPlaceholderText(option)
self._gridOptions()
I've gone from one book to another, one google search to another and I notice EVERY SINGLE ONE starts the main window in a completely different way.
I don't want to pick up bad habits so can someone please give me the best of these options and why its the better method. Below are all the ways i've seen it done
A)
class iFrame(wx.Frame):
def init(....):
wx.Frame._init_(...)
B)
class iFrame(wx.Frame):
def init(...):
super_init_(...)
C)
Then I see some that uses the Panel instead such as
class iPanel(wx.Panel)
def init(...):
wx.Panel.init(...)
D)
And even more confusing some are using the regular App class of wx
class iApp(wx.App):
def OnInit(self):
wx.Frame.init(...)
Forgive me if some of my structures are wrong but I'm recalling these off the top of my head, Question again...Which one of these, IF ANY is the best way to structure the GUI. It's hard following tutorials and books when they all do things in diff ways
edit: Sorry if format is not correct, but normally it works...
My favorite way to start wx application development is:
import wx
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.button = wx.Button(self.panel, label="Test")
self.sizer = wx.BoxSizer()
self.sizer.Add(self.button)
self.panel.SetSizerAndFit(self.sizer)
self.Show()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
See also this question, which is related.
Don't worry about it. You aren't going to ruin your future programming by making the wrong choice now.
None of the options you mention are wrong. They all do things differently because different applications have different requirements. No one way is best.
Just work on what you want and do what works for you, and once you have greater familiarity then you'll understand why different code does it differently.
I have learned the hard way that, as in every application, encapsulation is important. And peculiar to wxPython, the main frame object should have precisely one panel widget, plus optional menu bar, toolbar and status bar widgets. Nothing else.
This is my basic pattern for new wxPython applications:
(Updated 2019 Feb 07: Wx Phoenix and Python 3)
import wx
class MainFrame(wx.Frame):
"""Create MainFrame class."""
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(None, *args, **kwargs)
self.Title = 'Basic wxPython module'
self.SetMenuBar(MenuBar(self))
self.ToolBar = MainToolbar(self)
self.status_bar = StatusBar(self).status_bar
self.Bind(wx.EVT_CLOSE, self.on_quit_click)
panel = MainPanel(self)
sizer = wx.BoxSizer()
sizer.Add(panel)
self.SetSizerAndFit(sizer)
self.Centre()
self.Show()
def on_quit_click(self, event):
"""Handle close event."""
del event
wx.CallAfter(self.Destroy)
class MainPanel(wx.Panel):
"""Panel class to contain frame widgets."""
def __init__(self, parent, *args, **kwargs):
super(MainPanel, self).__init__(parent, *args, **kwargs)
"""Create and populate main sizer."""
sizer = wx.BoxSizer(wx.VERTICAL)
cmd_quit = wx.Button(self, id=wx.ID_EXIT)
cmd_quit.Bind(wx.EVT_BUTTON, parent.on_quit_click)
sizer.Add(cmd_quit)
self.SetSizer(sizer)
class MenuBar(wx.MenuBar):
"""Create the menu bar."""
def __init__(self, parent, *args, **kwargs):
super(MenuBar, self).__init__(*args, **kwargs)
# File menu
file_menu = wx.Menu()
self.Append(file_menu, '&File')
quit_menu_item = wx.MenuItem(file_menu, wx.ID_EXIT)
parent.Bind(wx.EVT_MENU, parent.on_quit_click, id=wx.ID_EXIT)
file_menu.Append(quit_menu_item)
class MainToolbar(wx.ToolBar):
"""Create tool bar."""
def __init__(self, parent, *args, **kwargs):
super(MainToolbar, self).__init__(parent, *args, **kwargs)
#quit_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT)
#self.AddTool(wx.ID_EXIT, 'Quit', wx.Bitmap(quit_bmp))
#self.SetToolShortHelp(wx.ID_EXIT, 'Quit')
#self.Bind(wx.EVT_TOOL, parent.on_quit_click, id=wx.ID_EXIT)
#self.Realize()
class StatusBar(object):
def __init__(self, parent):
"""Create status bar."""
self.status_bar = parent.CreateStatusBar()
if __name__ == '__main__':
"""Run the application."""
screen_app = wx.App()
main_frame = MainFrame()
screen_app.MainLoop()
In response to a comment by XilyummY, I have added this additional answer to show how the main classes can be organised in separate files.
This is my solution based on four files:
main.py: the main frame for the application and the application loader;
main_panel.py: the main panel for the application;
menu_bar.py: the menu bar definition for the frame;
tool_bar.py: the tool bar from the frame.
The code follows in this order:
main.py
import wx
from main_panel import MainPanel
from menu_bar import MenuBar
from tool_bar import MainToolbar
class MainFrame(wx.Frame):
"""Create MainFrame class."""
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(None, *args, **kwargs)
self.Title = 'Basic wxPython module'
self.SetMenuBar(MenuBar(self))
self.ToolBar = MainToolbar(self)
self.status_bar = StatusBar(self).status_bar
self.Bind(wx.EVT_CLOSE, self.on_quit_click)
panel = MainPanel(self)
sizer = wx.BoxSizer()
sizer.Add(panel)
self.SetSizerAndFit(sizer)
self.Centre()
self.Show()
def on_quit_click(self, event):
"""Handle close event."""
del event
wx.CallAfter(self.Destroy)
class StatusBar(object):
def __init__(self, parent):
"""Create status bar."""
self.status_bar = parent.CreateStatusBar()
if __name__ == '__main__':
"""Run the application."""
screen_app = wx.App()
main_frame = MainFrame()
screen_app.MainLoop()
main_panel.py
import wx
class MainPanel(wx.Panel):
"""Panel class to contain frame widgets."""
def __init__(self, parent, *args, **kwargs):
super(MainPanel, self).__init__(parent, *args, **kwargs)
"""Create and populate main sizer."""
sizer = wx.BoxSizer(wx.VERTICAL)
cmd_quit = wx.Button(self, id=wx.ID_EXIT)
cmd_quit.Bind(wx.EVT_BUTTON, parent.on_quit_click)
sizer.Add(cmd_quit)
self.SetSizer(sizer)
menu_bar.py
import wx
class MenuBar(wx.MenuBar):
"""Create the menu bar."""
def __init__(self, parent, *args, **kwargs):
super(MenuBar, self).__init__(*args, **kwargs)
# File menu
file_menu = wx.Menu()
self.Append(file_menu, '&File')
quit_menu_item = wx.MenuItem(file_menu, wx.ID_EXIT)
parent.Bind(wx.EVT_MENU, parent.on_quit_click, id=wx.ID_EXIT)
file_menu.Append(quit_menu_item)
tool_bar.py
import wx
class MainToolbar(wx.ToolBar):
"""Create tool bar."""
def __init__(self, parent, *args, **kwargs):
super(MainToolbar, self).__init__(parent, *args, **kwargs)
new_bmp = wx.ArtProvider.GetBitmap(wx.ART_NEW)
#preferences_bmp = wx.Bitmap('images/preferences.png')
quit_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT)
self.AddTool(wx.ID_NEW, 'New', new_bmp)
#self.AddTool(wx.ID_PREFERENCES, 'Preferences', preferences_bmp)
self.AddTool(wx.ID_EXIT, 'Quit', quit_bmp)
self.SetToolShortHelp(wx.ID_NEW, 'New ...')
self.SetToolShortHelp(wx.ID_PREFERENCES, 'Preferences ...')
self.SetToolShortHelp(wx.ID_EXIT, 'Quit')
self.Bind(wx.EVT_TOOL, parent.on_quit_click, id=wx.ID_EXIT)
self.Realize()