wxpython add splitter window to notebook page - wxwidgets

I have adapted this for my own use:
import wx
import wx.grid as gridlib
########################################################################
class LeftPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
grid = gridlib.Grid(self)
grid.CreateGrid(25,12)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(grid, 0, wx.EXPAND)
self.SetSizer(sizer)
########################################################################
class RightPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
txt = wx.TextCtrl(self)
########################################################################
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, title="Splitter Tutorial")
splitter = wx.SplitterWindow(self)
leftP = LeftPanel(splitter)
rightP = RightPanel(splitter)
# split the window
splitter.SplitVertically(leftP, rightP)
splitter.SetMinimumPaneSize(20)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(splitter, 1, wx.EXPAND)
self.SetSizer(sizer)
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
Now I want to add it as a page in a simple wxNotebook. Normally the notebook page would be set up something like:
p = wx.Panel(self)
nb = wx.Notebook(p)
page1 = module_name.module_Class(nb)
nb.AddPage(page1, "Tab label")
The splitter has two (three in my case) panel Classes but obviously I can't just call one of them. Can someone please explain how I can wrap it all up?
Thanks.

You just need to put the splitter widget on a panel in its own class. Then you can add your panel with the splitter in it to a notebook widget. Here's an example:
import wx
import wx.grid as gridlib
########################################################################
class LeftPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
grid = gridlib.Grid(self)
grid.CreateGrid(25,12)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(grid, 0, wx.EXPAND)
self.SetSizer(sizer)
########################################################################
class RightPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
txt = wx.TextCtrl(self)
########################################################################
class SplitterPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
splitter = wx.SplitterWindow(self)
leftP = LeftPanel(splitter)
rightP = RightPanel(splitter)
# split the window
splitter.SplitVertically(leftP, rightP)
splitter.SetMinimumPaneSize(20)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(splitter, 1, wx.EXPAND)
self.SetSizer(sizer)
########################################################################
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, title="Notebook Splitter Tutorial")
panel = wx.Panel(self)
notebook = wx.Notebook(panel)
splitter = SplitterPanel(notebook)
notebook.AddPage(splitter, 'Splitter')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
panel.SetSizer(sizer)
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()

Unless I'm missing something, you should just add the splitter itself as a page.

Related

cannot be associated with QComboBox

button of class Main don't connect with class Qcombobox of Signals
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import *
import sys
from PyQt5 import QtGui
class Signals(QWidget):
asignal = pyqtSignal(str)
def __init__(self):
super(Signals, self).__init__()
self.setGeometry(300, 250, 400, 300)
self.ii()
self.show()
def ii(self):
vbox = QVBoxLayout()
self.combo = QComboBox()
self.combo.addItem("Python")
self.combo.addItem("Java")
self.combo.addItem("C++")
self.combo.addItem("C#")
self.combo.addItem("Ruby")
self.buttom = QPushButton("Click")
self.buttom.clicked.connect(self.windown2)
vbox.addWidget(self.combo)
vbox.addWidget(self.buttom)
self.setLayout(vbox)
def do_something(self):
self.asignal.emit(self.combo.currentText())
def windown2(self):
self.ggpp = Main()
self.ggpp.show()
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
self.setGeometry(500,150, 600, 300)
vbox1 = QVBoxLayout()
self.buttom1 = QPushButton("Click")
self.buttom1.clicked.connect(self.coso1)
vbox1.addWidget(self.buttom1)
self.setLayout(vbox1)
def coso1(self):
s = Signals()
s.asignal.connect(lambda sig: print("self.combo.currentText()>>>>>" + sig))
s.do_something()
if __name__ == '__main__':
app = QApplication(sys.argv)
nals = Signals()
nals.show()
sys.exit(app.exec())
What you see happens because you're not using the existing instance of Signals, but you're creating a new one each time the button is clicked.
In your case, you could add a reference to the instance as an argument when you create the new window, so that you can correctly connect to its signal.
class Signals(QWidget):
# ...
def windown2(self):
self.ggpp = Main(self)
self.ggpp.show()
class Main(QWidget):
def __init__(self, signals):
super(Main, self).__init__()
self.signals = signals
self.signals.asignal.connect(self.coso1)
self.setGeometry(500,150, 600, 300)
vbox1 = QVBoxLayout()
self.buttom1 = QPushButton("Click")
self.buttom1.clicked.connect(self.signals.do_something)
vbox1.addWidget(self.buttom1)
self.setLayout(vbox1)
def coso1(self, sig):
print("self.combo.currentText()>>>>>" + sig)

PyQt5: drawing multiple rectangles using mouseEvents by implementing QGraphicsScene, QGraphicsView and QGraphicsItem [duplicate]

I have a scene like this
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(Scene, self).__init__(parent)
def mousePressEvent(self, event):
print('scene pressed')
self.wid = MyRect(event.pos(), event.pos())
self.addItem(self.wid)
self.wid.show()
I would like class MyRect(QtWidgets.QGraphicsRectItem) with painter, mouse event and so on to be a draggable rectangle.
all stuff in MyRect
So then I could have many Rectangle to the scene and even after draw line between them and so on (kind of diagram app), but keeping objects related editable options in MyRect, MyLine , ....
I thought :
class MyRect(QtWidgets.QGraphicsRectItem):
def __init__(self, begin, end, parent=None):
super().__init__(parent)
self.begin = begin
self.end = end
def paintEvent(self, event):
print('painting')
qp = QtGui.QPainter(self)
qp.drawRect(QtCore.QRect(self.begin, self.end))
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
But I does not work (paint event not initiated whereas mousepressed event in scene is intiated)
I did not find what I wanted through the web so started totry do it by myself. I'm pretty sure it is a must known starting point but I cannot find it
First of all a QGraphicsItem is not a QWidget, so it has those events and does not handle them directly, that's what QGraphicsView and QGraphicsScene do. For example you say that you want to have a moveable rectangle because that task is simple is QGraphicsView, it is not necessary to overwrite:
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
rect_item = QtWidgets.QGraphicsRectItem(QtCore.QRectF(0, 0, 100, 100))
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
scene.addItem(rect_item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
If you want to change the way you paint the rectangle you must overwrite the paint() method as shown below:
from PyQt5 import QtCore, QtGui, QtWidgets
class RectItem(QtWidgets.QGraphicsRectItem):
def paint(self, painter, option, widget=None):
super(RectItem, self).paint(painter, option, widget)
painter.save()
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(QtCore.Qt.red)
painter.drawEllipse(option.rect)
painter.restore()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
rect_item = RectItem(QtCore.QRectF(0, 0, 100, 100))
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
scene.addItem(rect_item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Update:
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(GraphicsScene, self).__init__(QtCore.QRectF(-500, -500, 1000, 1000), parent)
self._start = QtCore.QPointF()
self._current_rect_item = None
def mousePressEvent(self, event):
if self.itemAt(event.scenePos(), QtGui.QTransform()) is None:
self._current_rect_item = QtWidgets.QGraphicsRectItem()
self._current_rect_item.setBrush(QtCore.Qt.red)
self._current_rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.addItem(self._current_rect_item)
self._start = event.scenePos()
r = QtCore.QRectF(self._start, self._start)
self._current_rect_item.setRect(r)
super(GraphicsScene, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._current_rect_item is not None:
r = QtCore.QRectF(self._start, event.scenePos()).normalized()
self._current_rect_item.setRect(r)
super(GraphicsScene, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self._current_rect_item = None
super(GraphicsScene, self).mouseReleaseEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene =GraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

Resizing a QWindow to fit contents

The main window of my PyQt5 application is set up with a text label along the top above a custom canvas widget which displays an image:
from PyQt5 import QtCore, QtGui, QtWidgets
class Canvas(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.image = None
def paintEvent(self, event):
qp = QtGui.QPainter(self)
if self.image:
qp.drawImage(0, 0, self.image)
class Window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.canvas = Canvas()
self.label = QtWidgets.QLabel()
self.label.setText('foobar')
self.label.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Fixed)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.canvas)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
content = QtWidgets.QWidget()
content.setLayout(layout)
self.setCentralWidget(content)
self.load_image('a.jpg')
def load_image(self, filename):
image = QtGui.QImage(filename)
self.canvas.image = image
self.canvas.setFixedSize(image.width(), image.height())
self.update()
def keyPressEvent(self, event):
self.load_image('b.jpg')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
This looks like this, which is what I want:
When the canvas changes to display a smaller image, I want to shrink the window to fit accordingly. However, it looks like this:
It seems that the minimum size that I can give the window if I manually drag to resize it is the size that fits the contents, but why isn't it resizing to this automatically?
When a fixed size is set, it is used as sizeHint, and the latter is used by layouts to set the widget size. So the size of the canvas depends on the size of the widget, but you want the opposite. You must scale the image size to the window size:
from PyQt5 import QtCore, QtGui, QtWidgets
class Canvas(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
)
self.image = QtGui.QImage()
#property
def image(self):
return self._image
#image.setter
def image(self, image):
self._image = image
self.update()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
if not self.image.isNull():
image = self.image.scaled(
self.size(), QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation
)
qp.drawImage(0, 0, image)
class Window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.canvas = Canvas()
self.label = QtWidgets.QLabel("foobar")
self.label.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.canvas)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
content = QtWidgets.QWidget()
content.setLayout(layout)
self.setCentralWidget(content)
self.load_image("a.jpg")
def load_image(self, filename):
image = QtGui.QImage(filename)
self.canvas.image = image
def keyPressEvent(self, event):
self.load_image('b.jpg')
super().keyPressEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

how to show the cursor coordinates in wxPython when using wx.SplitterWindow

I'm trying to do something similar to that shown in this link to draw a cursor and report the data coordinates in the status bar. However, my code is a little bit different since I need to use wx.SplitterWindow to separate buttons and plots. Basically, in the main frame module I create the status bar, but the plot is generated in a separated module. See below my current code:
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar, \
wxc as wxc
import pylab
import wx
from numpy import arange, sin, pi
class data:
def __init__(self):
self.t = []
self.s = []
class Plot_Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# create some sizers
mainSizer = wx.BoxSizer(wx.VERTICAL)
checkSizer = wx.BoxSizer(wx.HORIZONTAL)
# create figrue
self.fig = Figure()
self.canvas = FigCanvas(self, -1, self.fig)
self.axes = self.fig.add_subplot(111)
# create the widgets
self.toggleMarker = wx.CheckBox(self, label="Show Marker")
# layout the widgets
mainSizer.Add(self.canvas, 1, wx.EXPAND)
checkSizer.Add(self.toggleMarker, 0, wx.ALL, 5)
mainSizer.Add(checkSizer)
self.SetSizer(mainSizer)
def draw_plot(self, data):
# Clear the previous figure
self.fig.clear()
# Redraw figure
self.axes = self.fig.add_subplot(111)
# Define data to plot
self.plot_data= self.axes.plot(data.t, data.s, linewidth=3, color='y',)[0]
# Draw Cursor or not
if self.toggleMarker.IsChecked():
# Note that event is a MplEvent
self.canvas.mpl_connect('motion_notify_event', self.UpdateStatusBar)
self.canvas.Bind(wx.EVT_ENTER_WINDOW, self.ChangeCursor)
self.canvas.draw()
def ChangeCursor(self, event):
self.canvas.SetCursor(wxc.StockCursor(wx.CURSOR_BULLSEYE))
def UpdateStatusBar(self, event):
if event.inaxes:
x, y = event.xdata, event.ydata
# self.statusBar.SetStatusText(("x= "+str(Pos.x)+" y="+str(Pos.y)))
class Button_Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# create the widgets
self.toggleStart = wx.Button(self, id=wx.ID_ANY, label="Plot data")
class ProportionalSplitter(wx.SplitterWindow):
def __init__(self,parent, id = -1, proportion=0.66, size = wx.DefaultSize, **kwargs):
wx.SplitterWindow.__init__(self,parent,id,wx.Point(0, 0),size, **kwargs)
self.SetMinimumPaneSize(50) #the minimum size of a pane.
self.proportion = proportion
if not 0 < self.proportion < 1:
raise ValueError, "proportion value for ProportionalSplitter must be between 0 and 1."
self.ResetSash()
self.Bind(wx.EVT_SIZE, self.OnReSize)
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged, id=id)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.firstpaint = True
def SplitHorizontally(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitHorizontally(self, win1, win2,
int(round(self.GetParent().GetSize().GetHeight() * self.proportion)))
def SplitVertically(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitVertically(self, win1, win2,
int(round(self.GetParent().GetSize().GetWidth() * self.proportion)))
def GetExpectedSashPosition(self):
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
return int(round(tot * self.proportion))
def ResetSash(self):
self.SetSashPosition(self.GetExpectedSashPosition())
def OnReSize(self, event):
"Window has been resized, so we need to adjust the sash based on self.proportion."
self.ResetSash()
event.Skip()
def OnSashChanged(self, event):
"We'll change self.proportion now based on where user dragged the sash."
pos = float(self.GetSashPosition())
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
self.proportion = pos / tot
event.Skip()
def OnPaint(self, event):
if self.firstpaint:
if self.GetSashPosition() != self.GetExpectedSashPosition():
self.ResetSash()
self.firstpaint = False
event.Skip()
class Main_Window(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title)
# Create a StatusBar at the bottom of the window
self.statusBar = wx.StatusBar(self, -1)
self.SetStatusBar(self.statusBar)
# Set plot panel
self.splitter = ProportionalSplitter(self,-1, 0.85)
self.ppanel = Plot_Panel(self.splitter)
self.ppanel.SetBackgroundColour('#ffffff')
# Set button panel
self.bpanel = Button_Panel(self.splitter)
# Set frame
self.splitter.SplitVertically(self.ppanel, self.bpanel)
self.Show(True)
self.Maximize(True)
# bind the widgets
self.ppanel.toggleMarker.Bind(wx.EVT_CHECKBOX, self.onToggleMarker)
self.bpanel.toggleStart.Bind(wx.EVT_BUTTON, self.onToggleStart)
# Set classes
self.data = data()
def onToggleMarker(self, event):
self.ppanel.draw_plot(self.data)
def onToggleStart(self, event):
self.data.t = arange(0.0, 1.0, 0.01)
self.data.s = sin(2*2*pi*self.data.t)
# plot data
self.ppanel.draw_plot(self.data)
def main():
app = wx.App(False)
frame = Main_Window(None, "GUI")
frame.Show()
app.MainLoop()
if __name__ == "__main__" :
main()
The plot is shown when the button "Plot data" is pressed. What I would like to do is to show the x and y position in the status bar when the checkbox "Show Marker" is checked (in a similar way as it is done in the code posted in the link), and stop when it is unchecked. But I'm not sure if it is possible to do it in my code due to having the definitions of the status bar and the plot in different modules. Any hint will be welcome.
What a joy to get a full, working example program with the question.
You just need to pass the base module as a parameter to self.ppanel
i.e.
self.ppanel = Plot_Panel(self.splitter, self)
then refer to that when updating the status bar, see below for references to base
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar, \
wxc as wxc
import pylab
import wx
from numpy import arange, sin, pi
class data:
def __init__(self):
self.t = []
self.s = []
class Plot_Panel(wx.Panel):
def __init__(self, parent, base):
wx.Panel.__init__(self, parent)
self.base = base
# create some sizers
mainSizer = wx.BoxSizer(wx.VERTICAL)
checkSizer = wx.BoxSizer(wx.HORIZONTAL)
# create figrue
self.fig = Figure()
self.canvas = FigCanvas(self, -1, self.fig)
self.axes = self.fig.add_subplot(111)
# create the widgets
self.toggleMarker = wx.CheckBox(self, label="Show Marker")
# layout the widgets
mainSizer.Add(self.canvas, 1, wx.EXPAND)
checkSizer.Add(self.toggleMarker, 0, wx.ALL, 5)
mainSizer.Add(checkSizer)
self.SetSizer(mainSizer)
def draw_plot(self, data):
# Clear the previous figure
self.fig.clear()
# Redraw figure
self.axes = self.fig.add_subplot(111)
# Define data to plot
self.plot_data= self.axes.plot(data.t, data.s, linewidth=3, color='y',)[0]
# Draw Cursor or not
if self.toggleMarker.IsChecked():
# Note that event is a MplEvent
self.canvas.mpl_connect('motion_notify_event', self.UpdateStatusBar)
self.canvas.Bind(wx.EVT_ENTER_WINDOW, self.ChangeCursor)
self.canvas.draw()
def ChangeCursor(self, event):
self.canvas.SetCursor(wxc.StockCursor(wx.CURSOR_BULLSEYE))
def UpdateStatusBar(self, event):
if event.inaxes:
x, y = event.xdata, event.ydata
self.base.statusBar.SetStatusText(("x= "+str(x)+" y="+str(y)))
class Button_Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# create the widgets
self.toggleStart = wx.Button(self, id=wx.ID_ANY, label="Plot data")
class ProportionalSplitter(wx.SplitterWindow):
def __init__(self,parent, id = -1, proportion=0.66, size = wx.DefaultSize, **kwargs):
wx.SplitterWindow.__init__(self,parent,id,wx.Point(0, 0),size, **kwargs)
self.SetMinimumPaneSize(50) #the minimum size of a pane.
self.proportion = proportion
if not 0 < self.proportion < 1:
raise ValueError, "proportion value for ProportionalSplitter must be between 0 and 1."
self.ResetSash()
self.Bind(wx.EVT_SIZE, self.OnReSize)
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged, id=id)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.firstpaint = True
def SplitHorizontally(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitHorizontally(self, win1, win2,
int(round(self.GetParent().GetSize().GetHeight() * self.proportion)))
def SplitVertically(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitVertically(self, win1, win2,
int(round(self.GetParent().GetSize().GetWidth() * self.proportion)))
def GetExpectedSashPosition(self):
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
return int(round(tot * self.proportion))
def ResetSash(self):
self.SetSashPosition(self.GetExpectedSashPosition())
def OnReSize(self, event):
"Window has been resized, so we need to adjust the sash based on self.proportion."
self.ResetSash()
event.Skip()
def OnSashChanged(self, event):
"We'll change self.proportion now based on where user dragged the sash."
pos = float(self.GetSashPosition())
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
self.proportion = pos / tot
event.Skip()
def OnPaint(self, event):
if self.firstpaint:
if self.GetSashPosition() != self.GetExpectedSashPosition():
self.ResetSash()
self.firstpaint = False
event.Skip()
class Main_Window(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title)
# Create a StatusBar at the bottom of the window
self.statusBar = wx.StatusBar(self, -1)
self.SetStatusBar(self.statusBar)
# Set plot panel
self.splitter = ProportionalSplitter(self,-1, 0.85)
self.ppanel = Plot_Panel(self.splitter, self)
self.ppanel.SetBackgroundColour('#ffffff')
# Set button panel
self.bpanel = Button_Panel(self.splitter)
# Set frame
self.splitter.SplitVertically(self.ppanel, self.bpanel)
self.Show(True)
self.Maximize(True)
# bind the widgets
self.ppanel.toggleMarker.Bind(wx.EVT_CHECKBOX, self.onToggleMarker)
self.bpanel.toggleStart.Bind(wx.EVT_BUTTON, self.onToggleStart)
# Set classes
self.data = data()
def onToggleMarker(self, event):
self.ppanel.draw_plot(self.data)
def onToggleStart(self, event):
self.data.t = arange(0.0, 1.0, 0.01)
self.data.s = sin(2*2*pi*self.data.t)
# plot data
self.ppanel.draw_plot(self.data)
def main():
app = wx.App(False)
frame = Main_Window(None, "GUI")
frame.Show()
app.MainLoop()
if __name__ == "__main__" :
main()

How to access from parent method to child object in PyQt5?

I have a button in widget widget which is a central widget of MyApp class, which inherits from QMainWindow. I have connected widget's button to MyApp's method called logic. This is fine, because if I write a method in my main class MyApp(QMainWindow):
def logic(self):
sender = self.sender()
print(sender)
... and click the button I get a message:
PyQt5.QtWidgets.QPushButton object at 0x7ff92e19eaf8
My question is how can I from MyApp's method called logic access to its child object like MyApp.widget.saltLine which is a QLineEdit object? I need to read that line.
class MyApp(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.initui()
def initui(self):
self.setMinimumSize(500, 150)
# create central widget
widget = QWidget()
# lines for entering data
widget.saltLabel = QLabel("Salt:")
widget.hashSunkenLabel = QLabel()
widget.passwordLine = QLineEdit()
widget.resultButton = QPushButton("&Calculate", self)
# set layout
grid = QGridLayout()
grid.addWidget(widget.saltLabel, 0, 0)
grid.addWidget(widget.passwordLine, 1, 0)
grid.addWidget(widget.hashSunkenLabel, 2, 0)
grid.addWidget(widget.resultButton, 2, 1)
# set widget a grid layout and set widget
# as central widget of QMainWindows
widget.setLayout(grid)
self.setCentralWidget(widget)
# don't know how should this look like
widget.resultButton.clicked.connect(self.logic)
def logic(self):
salt = self.saltLine.text()
password = self.passwordLine.text()
resulting_hash = crypt.crypt(password, salt)
self.hashSunkenLabel.setText(resulting_hash)
Or am I definig centralwidget widget wrong? If I do it without a central widget it works fine:
#!/usr/bin/env python3
import sys
import crypt
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QMainWindow, QLabel, QLineEdit, QPushButton,
QWidget, QApplication, QSystemTrayIcon, QFrame, QGridLayout)
class MyApp(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.saltLabel = QLabel("Salt:")
self.saltLine = QLineEdit()
self.saltLine.setPlaceholderText("e.g. $6$xxxxxxxx")
self.passwordLabel = QLabel("Password:")
self.passwordLine = QLineEdit()
self.hashLabel = QLabel("Hash:")
self.hashSunkenLabel = QLabel()
# widget.hashSunkenLabel.setFrameStyle(QFrame.Box | QFrame.Sunken)
self.hashSunkenLabel.setFrameShadow(QFrame.Sunken)
self.resultButton = QPushButton("&Calculate", self)
self.resultButton.setMaximumSize(100, 50)
self.initui()
def initui(self):
# main window size, title and icon
self.setGeometry(300, 300, 500, 150)
self.setWindowTitle("Password hash calculator | Linux")
# set layout
grid = QGridLayout()
grid.addWidget(self.passwordLabel, 0, 0)
grid.addWidget(self.passwordLine, 0, 1)
grid.addWidget(self.saltLabel, 1, 0)
grid.addWidget(self.saltLine, 1, 1)
grid.addWidget(self.resultButton, 2, 1)
grid.addWidget(self.hashLabel, 3, 0)
grid.addWidget(self.hashSunkenLabel, 3, 1)
self.setLayout(grid)
self.resultButton.clicked.connect(self.logic)
def logic(self):
"""
Calculates hash from salt and password
"""
salt = self.saltLine.text()
password = self.passwordLine.text()
resulting_hash = crypt.crypt(password, salt)
self.hashSunkenLabel.setText(resulting_hash)
# sender = self.sender()
# print(sender)
# print(dir(MyApp))
def main():
app = QApplication(sys.argv)
instance = MyApp()
instance.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()