wxPython: How to bind the button? - binding

I just started with wxPython and have problems with the binding.
Usually the examples I find to bind a button event are done withself.Bind(wx.EVT_BUTTON, self.OnWhatEverFunction, button). I have a frame with a panel inside and a button and what ever I tried, the outcome is always a flashing frame that appears in a split second and that's it. Since the code isn't that much I attach it here and hope one of you can show me the way out of my little problem.
Thanks in advance,
Thomas
#!/usr/bin/python
import wx
class CPFSFrame(wx.Frame):
def __init__(self, parent, title):
super(CPFSFrame, self).__init__(parent, title=title,
style = wx.BORDER | wx.CAPTION | wx.SYSTEM_MENU | wx.CLOSE_BOX,
size=(350, 200))
panel = wx.Panel(self, -1)
pNumberLabel = wx.StaticText(panel, -1, 'Project number: ')
pNumberText = wx.TextCtrl(panel, -1, '', size=(175, -1))
pNumberText.SetInsertionPoint(0)
pNameLabel = wx.StaticText(panel, -1, 'Project name: ')
pNameText = wx.TextCtrl(panel, -1, '', size=(175, -1))
pNameText.SetInsertionPoint(0)
pButton = wx.Button(panel, label='Create')
pButton.Bind(wx.EVT_BUTTON, self.OnCreate)
sizer = wx.FlexGridSizer(cols=2, hgap=6, vgap=6)
sizer.AddMany([pNumberLabel, pNumberText, pNameLabel, pNameText, pButton])
panel.SetSizer(sizer)
statusBar = self.CreateStatusBar()
def OnCreate(self, evt):
self.Close(True)
self.Centre()
self.Show()
if __name__ == '__main__':
app = wx.App()
CPFSFrame(None, title='Create New Project Folder Structure')
app.MainLoop()

You need to put the "self.Show()" method in the init section of the code. At first I thought OnCreate() was running and if it was, then it would just close the frame immediately so you wouldn't see anything. I would call it OnClose instead. Here's a working example:
import wx
class CPFSFrame(wx.Frame):
def __init__(self, parent, title):
super(CPFSFrame, self).__init__(parent, title=title,
style = wx.BORDER | wx.CAPTION | wx.SYSTEM_MENU | wx.CLOSE_BOX,
size=(350, 200))
panel = wx.Panel(self, -1)
pNumberLabel = wx.StaticText(panel, -1, 'Project number: ')
pNumberText = wx.TextCtrl(panel, -1, '', size=(175, -1))
pNumberText.SetInsertionPoint(0)
pNameLabel = wx.StaticText(panel, -1, 'Project name: ')
pNameText = wx.TextCtrl(panel, -1, '', size=(175, -1))
pNameText.SetInsertionPoint(0)
pButton = wx.Button(panel, label='Create')
pButton.Bind(wx.EVT_BUTTON, self.OnClose)
sizer = wx.FlexGridSizer(cols=2, hgap=6, vgap=6)
sizer.AddMany([pNumberLabel, pNumberText, pNameLabel, pNameText, pButton])
panel.SetSizer(sizer)
statusBar = self.CreateStatusBar()
self.Show()
def OnClose(self, evt):
self.Close(True)
if __name__ == '__main__':
app = wx.App()
CPFSFrame(None, title='Create New Project Folder Structure')
app.MainLoop()

Related

PySide & QWT object disable/destroy

I am just learning OOP and PySide. I have created a code as below.
The application doesn't do anything much (it's a development project in learning stages).
import numpy as np
import sys
from qtpy.QtWidgets import (
QWidget,
QMainWindow,
QVBoxLayout,
QAction,
QMenu,
QLabel,
QApplication,
QMessageBox,
QDesktopWidget,
)
from qtpy.QtCore import Qt, Slot, QPoint, QObject
from qwt import (
QwtPlot,
QwtPlotMarker,
QwtPlotGrid,
QwtLegend,
QwtPlotCurve,
QwtLegendData,
)
class contexMenuHelper(QObject):
def __init__(self, plot, legend, legendItem):
super(contexMenuHelper, self).__init__()
self.plot = plot
self.legend = legend
self.legendItem = legendItem
#Slot(QPoint)
def contextMenuSlot(self, pos):
context = QMenu(self.legendItem)
context.addAction(QAction("Delete", self))
context.exec_(self.legendItem.mapToGlobal(pos))
class Plot(QwtPlot, QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAxisTitle(QwtPlot.xBottom, "X-axis")
self.setAxisTitle(QwtPlot.yLeft, "Y-axis")
self.setCanvasBackground(Qt.white)
self.setAxisScale(QwtPlot.yLeft, -2, 2)
QwtPlotGrid.make(self, color=Qt.lightGray, width=0, style=Qt.DotLine)
legend = QwtLegend()
legend.setDefaultItemMode(QwtLegendData.Checkable)
self.insertLegend(legend, QwtPlot.RightLegend)
x = np.arange(-5.0, 5.0, 0.1)
curves = []
curves.append(
QwtPlotCurve.make(
x, np.cos(x), "Cosinus", self, linecolor="red", antialiased=True
)
)
curves.append(
QwtPlotCurve.make(
x, np.sin(x), "Sinus", self, linecolor="blue", antialiased=True
)
)
self.helpers = dict()
for a in curves:
legend.legendWidget(a).setContextMenuPolicy(Qt.CustomContextMenu)
h = contexMenuHelper(self, legend, legend.legendWidget(a))
self.helpers[a] = h
legend.legendWidget(a).customContextMenuRequested.connect(h.contextMenuSlot)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.HLine,
color="black",
plot=self,
)
for keys, value in self.helpers.items():
print(keys)
print(value)
# insert a vertical marker at x = 0
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.VLine,
color="black",
plot=self,
)
legend.checked.connect(self.showCurve)
self.replot()
#Slot(object, bool, int)
def showCurve(self, obj, condition, num):
obj.setVisible(not condition)
self.replot()
#Slot(object, bool, int)
def __del__(self, obj, condition):
print('Destructor called, vehicle deleted.')
class SimplePlot(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.setLayout(layout)
plot = Plot()
plot.setTitle("Trigonometric")
self.setWindowTitle("Trigonometric")
layout.addWidget(plot)
label = QLabel("Press the legend to en/disable a curve")
layout.addWidget(label)
self.center()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def closeEvent(self, event):
reply = QMessageBox.question(
self,
"Message",
"Are you sure to quit?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SimplePlot()
window.show()
window.resize(800, 600)
sys.exit(app.exec_())
I made the active legend and the context menu:
I want to make it so that when I select "Delete" from the context menu, the corresponding function waveform in the graph and the corresponding object in the legend will be deleted.
I have implemented it as follows. Perhaps someone will find my thinking useful. It works correctly and as I expected although there is a tiny error in the operation itself .
Do you see what error I mean?
class contexMenuHelper(QObject):
def __init__(self, plot, legend, legendItem):
super(contexMenuHelper, self).__init__()
self.plot = plot
self.legend = legend
self.legendItem = legendItem
self.emlSel = QAction("Delete")
#Slot(QPoint)
def contextMenuSlot(self, pos):
context = QMenu(self.legendItem)
context.addAction(self.emlSel)
context.exec_(self.legendItem.mapToGlobal(pos))
self.emlSel.triggered.connect(self.destroy())
#Slot()
def destroy(self):
QwtPlotCurve.detach(self.legend)
class Plot(QwtPlot, QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAxisTitle(QwtPlot.xBottom, "X-axis")
self.setAxisTitle(QwtPlot.yLeft, "Y-axis")
self.setCanvasBackground(Qt.white)
self.setAxisScale(QwtPlot.yLeft, -2, 2)
QwtPlotGrid.make(self, color=Qt.lightGray, width=0, style=Qt.DotLine)
legend = QwtLegend()
legend.setDefaultItemMode(QwtLegendData.Checkable)
legend.resize(100,100)
self.insertLegend(legend, QwtPlot.RightLegend)
x = np.arange(-5.0, 5.0, 0.1)
curves = []
curves.append(
QwtPlotCurve.make(
x, np.cos(x), "Cosinus", self, linecolor="red", antialiased=True
)
)
curves.append(
QwtPlotCurve.make(
x, np.sin(x), "Sinus", self, linecolor="blue", antialiased=True
)
)
self.helpers = dict()
for a in curves:
legend.legendWidget(a).setContextMenuPolicy(Qt.CustomContextMenu)
h = contexMenuHelper(self, a, legend.legendWidget(a))
self.helpers[a] = h
legend.legendWidget(a).customContextMenuRequested.connect(h.contextMenuSlot)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.HLine,
color="black",
plot=self,
)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.VLine,
color="black",
plot=self,
)
legend.checked.connect(self.showCurve)
self.replot()
#Slot(object, bool, int)
def showCurve(self, obj, condition, num):
obj.setVisible(not condition)
self.replot()
class SimplePlot(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.setLayout(layout)
plot = Plot()
plot.setTitle("Trigonometric")
self.setWindowTitle("Trigonometric")
layout.addWidget(plot)
label = QLabel("Press the legend to en/disable a curve")
layout.addWidget(label)
self.center()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def closeEvent(self, event):
reply = QMessageBox.question(
self,
"Message",
"Are you sure to quit?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SimplePlot()
window.show()
window.resize(850, 600)
sys.exit(app.exec_())

How can I shift all of the tables down after removing a table?

In this code:
t = {
num = '',
}
t[0].num = '0'
t[1].num = '1'
t[2].num = '2'
Is there a way for me to delete t[0], then shift all of the table's values down, so that afterword it looks like this:
t[0].num = '1'
t[1].num = '2'
Example with imaginary functions:
t = {
num = '',
}
t[0].num = '0'
t[1].num = '1'
t[2].num = '2'
for i=0,tableLength(t) do
print(t[i])
end
--Output: 012
remove(t[0])
for i=0,tableLength(t) do
print(t[i])
end
--Output: 12
t = {
num = '',
}
t[0].num = '0'
t[1].num = '1'
t[2].num = '2'
This code will cause errors for indexing t[0], a nil value.
t only has one field and that is t.num
You need to do something like this:
t = {}
for i = 0, 2 do
t[i] = {num = tostring(i)}
end
if you want to create the desired demo table.
As there are many useful functions in Lua that assume 1-based indexing you I'd recommend starting at index 1.
local t = {1,2,3,4,5}
Option 1:
table.remove(t, 1)
Option 2:
t = {table.unpack(t, 2, #t)}
Option 3:
t = table.move(t, 2, #t, 1, t)
t[#t] = nil
Option 4:
for i = 1, #t-1 do
t[i] = t[i+1]
end
t[#t] = nil
There are more options. I won't list them all. Some do it in place, some result in new table objects.
As stated in this answer, by creating a new table using the result of table.unpack:
t = {table.unpack(t, 1, #t)}

how to create a print dialog box in python3

I am novice in python and on my way to completing my first desktop application. I have some reports but don't know how to send them to printer with the click of a button
Below is an example I got from PyQT's website. It helped me out quite a bit. You will have to install pip and PyQT5
from the command prompt type:
pip install PyQT5
import sys, os
from PyQt5 import QtCore, QtWidgets, QtPrintSupport
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.setWindowTitle('Document Printer')
self.editor = QtWidgets.QTextEdit(self)
self.editor.textChanged.connect(self.handleTextChanged)
self.buttonOpen = QtWidgets.QPushButton('Open', self)
self.buttonOpen.clicked.connect(self.handleOpen)
self.buttonPrint = QtWidgets.QPushButton('Print', self)
self.buttonPrint.clicked.connect(self.handlePrint)
self.buttonPreview = QtWidgets.QPushButton('Preview', self)
self.buttonPreview.clicked.connect(self.handlePreview)
layout = QtWidgets.QGridLayout(self)
layout.addWidget(self.editor, 0, 0, 1, 3)
layout.addWidget(self.buttonOpen, 1, 0)
layout.addWidget(self.buttonPrint, 1, 1)
layout.addWidget(self.buttonPreview, 1, 2)
self.handleTextChanged()
def handleOpen(self):
path = QtWidgets.QFileDialog.getOpenFileName(
self, 'Open file', '',
'HTML files (*.html);;Text files (*.txt)')[0]
if path:
file = QtCore.QFile(path)
if file.open(QtCore.QIODevice.ReadOnly):
stream = QtCore.QTextStream(file)
text = stream.readAll()
info = QtCore.QFileInfo(path)
if info.completeSuffix() == 'html':
self.editor.setHtml(text)
else:
self.editor.setPlainText(text)
file.close()
def handlePrint(self):
dialog = QtPrintSupport.QPrintDialog()
if dialog.exec_() == QtWidgets.QDialog.Accepted:
self.editor.document().print_(dialog.printer())
def handlePreview(self):
dialog = QtPrintSupport.QPrintPreviewDialog()
dialog.paintRequested.connect(self.editor.print_)
dialog.exec_()
def handleTextChanged(self):
enable = not self.editor.document().isEmpty()
self.buttonPrint.setEnabled(enable)
self.buttonPreview.setEnabled(enable)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.resize(640, 480)
window.show()
sys.exit(app.exec_())

Kivy: FileChooser redrawing

I want to use FileChooser for basic operation. I select folder using FileChooser, I will remove it using own function. Folder is removed and I want to show new contents of disc but contents is incorrect How do I show current right contents of disc?
My question based from problem with next code. When I removed folder in root directory, contens of FileChooserListView was incorrect. Source of problem was in discname. Last symbol in disc name wasn't '**'. After added this ( function delete_dir() isn't problem.
Builder.load_string('''
<ConfirmPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: root.dispatch('on_answer','yes')
Button:
text: 'No'
on_release: root.dispatch('on_answer', 'no')
''')
class ConfirmPopup(GridLayout):
text = StringProperty('')
def __init__(self,**kwargs):
self.register_event_type('on_answer')
super(ConfirmPopup,self).__init__(**kwargs)
def on_answer(self, *args):
pass
class PopupYesNo(GridLayout):
def __init__(self, save_as, task):
self.save_as = save_as
self.task = task
def show_widget(self, question):
self.content = ConfirmPopup(text= question)
self.content.bind(on_answer = self._on_answer)
self.popup = Popup(title="Answer Question",
content=self.content,
size_hint=(None, None),
size=(480,400),
auto_dismiss= False)
self.popup.open()
def _on_answer(self, instance, answer):
if answer == 'yes':
self.save_as.act_task()
self.popup.dismiss()
return
class SaveAs(BoxLayout):
def __init__(self, **kwargs):
super(SaveAs,self).__init__(**kwargs)
self.orientation = 'vertical'
self.fichoo = FileChooserListView(size_hint_y = 0.8)
self.add_widget(self.fichoo)
control = GridLayout(cols = 5, row_force_default=True, row_default_height=35, size_hint_y = 0.14)
self.tein_dir = TextInput(size_hint_x = None, width = 350)
self.tein_dir.multiline = False
bt_del_dir = Button(text = 'Remove',size_hint_x = None, width = 80)
bt_del_dir.bind(on_release = self.on_delete_dir)
control.add_widget(self.tein_dir)
control.add_widget(bt_del_dir)
self.fichoo.bind(path = self.on_path_select)
self.add_widget(control)
return
def on_path_select(self, inst, val):
self.tein_dir.text = str(self.fichoo.path)
return
def on_delete_dir(self, obj):
question = 'Do You want to remove: '+ self.tein_dir.text+ '?'
self.act_task = self.delete_dir
popup = PopupYesNo(self, SaveAs.delete_dir)
popup.show_widget(question)
return
def delete_dir(self):
pos = self.fichoo.path.rfind('\\', 0, len(self.fichoo.path))
new_path = str(self.fichoo.path)[0:pos]
if new_path[-1] == ':':
new_path += '\\' # last symbol in discname is '\'
self.tein_dir.text = new_path
os.chdir(new_path)
shutil.rmtree(str(self.fichoo.path))
self.fichoo.path = new_path
return
class ExplorerApp(App):
def build(self):
self.save_as = SaveAs()
return self.save_as
if __name__ == '__main__':
ExplorerApp().run()

wxPython: write SQL command results to outputbox

I'm trying to get back into Python and I'm once again stuck with this problem I've had before of making objects accessible to one another. In this simple example I am displaying a panel with a button and a text box. Clicking on the text box calls a function which queries a database and returns a cursor with the retrieved data. I need to make it so that either the LookupSQL function or the ShowClientData function can write this output, in a loop, to the Text box. The TextBox (outputBox) is unknown to any other functions currently. How do I make it so that the other functions know what it is?
import wx
import pypyodbc
conn = pypyodbc.connect(driver='{SQL Server}', server='.', database='TheDB', uid='sa', pwd='Pass')
class Audit(wx.Frame):
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
self.InitUI()
def InitUI(self):
panel = wx.Panel(self)
hbox = wx.BoxSizer()
sizer = wx.GridSizer(6,1,2,2)
btn1 = wx.Button(panel, label='Clients')
outputBox = wx.TextCtrl(panel, -1, style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
sizer.AddMany([btn1, btn2, btn3, btn4, btn5, btn6])
hbox.Add(sizer, 0, wx.ALL, 15)
hbox.Add(outputBox, 1, wx.EXPAND)
panel.SetSizer(hbox)
btn1.Bind(wx.EVT_BUTTON, self.ShowClientData)
self.SetSize((800, 600))
self.SetTitle('Audit View')
self.Centre()
self.Show(True)
def ShowClientData(self, event):
SQL = 'select * from V_UpdatedClient'
recursor = lookupSQL(SQL)
for row in recursor:
rChange = row[0]
rItemType = row[1]
rPK = row[2]
rItemCode = row[3]
rFieldName = row[4]
rOldValue = row[5]
rNewValue = row[6]
rUpdateDate = row[7]
rUserName = row[8]
print('%s %s %s %s %s %s %s %s %s' % (rChange, rItemType, rPK, rItemCode, rFieldName, rOldValue, rNewValue, rUpdateDate, rUserName))
def lookupSQL(SQLString):
cursor = conn.cursor()
cursor.execute(SQLString)
return cursor
cursor.close()
def main():
ex = wx.App()
Audit(None)
ex.MainLoop()
if __name__ == '__main__':
main()
What you are looking for is called data attributes.
self.outputBox = wx.TextCtrl(panel, -1, style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
And then within ShowClientData you can write
self.outputBox.AppendText("some text")
As long as you have that self reference, you can access its attributes.
Edit:
When you do the above change, you can't refer to the text box by just outputBox anymore, you should instead access it via self:
hbox.Add(self.outputBox, 1, wx.EXPAND)
Declaring it as globally is very bad!

Resources