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
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_())
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)}
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_())
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()
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!