When I click my button on the second window, it connects to the top class and successfully prints "yes", when I hit the stop button that binds to self.Hault, the timer stops and it prints. Is there something silly I am missing when I hit the button on the second window that would make it print, but not perform the action?
class Main(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
#timer commands
self.Start_2.clicked.connect(self.start_time)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.Time)
self.Stop.clicked.connect(self.Hault)
self.window2 = None
def Hault(self):
self.timer.stop()
print ('yes')
class Form(QtGui.QDialog, Ui_Form):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.setupUi(self)
self.main1 = Main()
self.pop_up_pushButton.clicked.connect(self.main1.Hault)
Related
I would like to test a GUI I have been working on written using wxpython without requiring interaction so that I can continue development and test regularly and easily. My problem is that whenever I test a function that opens a modal dialog the testing pauses and won't continue until I manually press the OK button. I can't seem to discover a reason for this, but it is connected to the native wxpython dialog types like wx.MessageDialog and wx.FileDialog because if I use show modal on a custom class that inherits from wx.Dialog then I can use wx.CallAfter to respond to the dialog fine. So is there a good way to respond to these modal dialogs?
The dialogs don't work during normal operation of the GUI if I just use Show instead of ShowModal so I can't simply not make the dialogs modal.
Bellow is a bit of code to test this outlining this basic problem. It runs two tests one that opens a wx.MessageDialog and one that opens a custom dialog both I attempt to close with wx.CallAfter.
import unittest
import wx
class MyDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, -1, 'Test')
wx.Button(self, wx.ID_OK)
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Flintstones',
size=(340, 200))
panel = wx.Panel(self)
panel.SetBackgroundColour("White")
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
self.button = wx.Button(panel, id=-1, label='custom dlg', size=(75, 25))
self.Bind(wx.EVT_BUTTON, self.create_dlg, self.button)
self.msgbutton = wx.Button(panel, id=-1, label='wx message dlg', size=(75, 25))
self.Bind(wx.EVT_BUTTON, self.create_msgdlg, self.msgbutton)
def OnCloseWindow(self, event):
self.Destroy()
def create_dlg(self, event):
TEXT="specimens interpretations are saved"
self.dlg = MyDialog(self)
result = self.dlg.ShowModal()
if result == wx.ID_OK:
print("custom dlg: okay pressed")
else:
print("custom: okay not pressed")
self.dlg.Destroy()
def create_msgdlg(self, event):
TEXT="specimens interpretations are saved"
self.dlg = wx.MessageDialog(self, caption="Saved",message=TEXT,style=wx.OK|wx.CANCEL)
result = self.dlg.ShowModal()
if result == wx.ID_OK:
print("message dlg: okay pressed")
else:
print("message dlg: okay not pressed")
self.dlg.Destroy()
class TestMyDialog(unittest.TestCase):
def setUp(self):
self.app = wx.App()
self.frame = MyFrame(None,1)
self.frame.Show()
def tearDown(self):
wx.CallAfter(self.app.Exit)
self.app.MainLoop()
def testDialog(self):
evt = wx.CommandEvent(wx.EVT_BUTTON.typeId,self.frame.button.GetId())
def clickOK():
clickEvent = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, wx.ID_OK)
self.frame.dlg.ProcessEvent(clickEvent)
wx.CallAfter(clickOK)
self.frame.ProcessEvent(evt)
def testMsgDialog(self):
evt = wx.CommandEvent(wx.EVT_BUTTON.typeId,self.frame.msgbutton.GetId())
def clickOK():
clickEvent = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, wx.ID_OK)
self.frame.dlg.ProcessEvent(clickEvent)
wx.CallAfter(clickOK)
self.frame.ProcessEvent(evt)
if __name__ == '__main__':
unittest.main()
The user enters values using line edits on the MyWidget screen and then presses the Enter button. This opens the MyDialog screen on which data will be plotted when the Run button is pressed. How can I make the line edit data accessible to run in MyDialog for plotting? Or, is there a better way of doing this which wouldn't require passing variables between classes? My program is based on this answer.
from PyQt4 import QtCore, QtGui, uic
# Import Qt widgets
from matplotlib.backends.backend_qt4agg \
import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg \
import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
GUI_FILE = "Minimal_UI.ui" # GUI with line edit and 'enter' button
form_class = uic.loadUiType(GUI_FILE)[0]
class MyWidget(QtGui.QWidget, form_class):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.setupUi(self)
self.pushButton_Enter.clicked.connect(self.on_pushButton_clicked)
self.dialogTextBrowser = MyDialog(self)
#QtCore.pyqtSlot()
def on_pushButton_clicked(self):
# I'd like to be able to pass Temp_0 to the run method
self.Temp_0 = self.lineEdit_Temp_0.text()
self.dialogTextBrowser.exec_()
class MyDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
self.toolbar = NavigationToolbar(self.canvas, self)
self.run_button = QtGui.QPushButton('Run')
self.run_button.clicked.connect(self.run)
self.stop_button = QtGui.QPushButton('Stop')
self.stop_button.clicked.connect(self.stop)
layout = QtGui.QVBoxLayout()
# Widgets are stacked in the order they are added
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.run_button)
layout.addWidget(self.stop_button)
self.setLayout(layout)
def run(self):
# Create axes
ax = self.fig.add_subplot(111)
# Discard the old graphs
ax.hold(False)
# Plot data--I'd like to be able to use line edit data here
ax.plot([1, 2, 3, 4], '*-')
# Refresh canvas
self.canvas.draw()
def stop(self):
print 'Stop Pressed'
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWidget')
main = MyWidget()
main.show()
sys.exit(app.exec_())
MyDialog constructor has an attribute parent.
With the code bellow, you create an instance of MyDialog with MyWidget as a parent:
self.dialogTextBrowser = MyDialog(self)
Two ways for a widget to access data from it's parent:
Use the parent attribute in the __init__ function
self.lineEditData=parent.lineEdit.text()
Use the parent() method anywhere
def run(self):
self.lineEditData=self.parent().lineEdit.text()
I say it depends on how your suppose to use the application. If your suppose to fill the lineEdit once click and get a plot, I would use the parent attribute or directly pass the data in the __init__ function.
But if the user can go back to the lineEdit, change something, and click "run" again, then you should use the parent() method in run.
I want to click on a menubar "Tools" menu to change my window content completely. How do I do this with PySide? Should I call QAction and set new widget as a central one with a parent of an old window? I'm beginner in python and english too. So far I've created only one-window application.
Firstly, I would define each tool within its own subclass of a QWidget. Secondly, I would add an instance of each tool widget thus created to the layout of the central widget of the main window. Finally, I would add actions to the menuBar and connect them to methods to show and hide the tools as desired.
Below is an example to show how this can be done with 2 different tools:
from PySide import QtGui
import sys
class myApplication(QtGui.QMainWindow):
def __init__(self, parent=None):
super(myApplication, self).__init__(parent)
self.setWindowTitle('No Tool is Selected')
#---- create instance of each tool widget ----
self.tool1 = Tool1(self)
self.tool2 = Tool2(self)
#---- layout for central widget ----
centralWidget = QtGui.QWidget()
centralLayout = QtGui.QGridLayout()
centralLayout.addWidget(self.tool1, 0, 0)
centralLayout.addWidget(self.tool2, 1, 0)
centralWidget.setLayout(centralLayout)
self.setCentralWidget(centralWidget)
#---- set the menu bar ----
contentMenu = self.menuBar().addMenu(("Tools"))
contentMenu.addAction('show Tool 1', self.show_Tool1)
contentMenu.addAction('show Tool 2', self.show_Tool2)
contentMenu.addAction('show All', self.show_All)
def show_Tool1(self):
self.tool1.show()
self.tool2.hide()
self.setWindowTitle('Tool #1 is Selected')
def show_Tool2(self):
self.tool1.hide()
self.tool2.show()
self.setWindowTitle('Tool #2 is Selected')
def show_All(self):
self.tool1.show()
self.tool2.show()
self.setWindowTitle('All Tools are Selected')
class Tool1(QtGui.QWidget):
def __init__(self, parent=None):
super(Tool1, self).__init__(parent)
layout = QtGui.QGridLayout()
layout.addWidget(QtGui.QPushButton('Tool #1'))
self.setLayout(layout)
self.hide()
class Tool2(QtGui.QWidget):
def __init__(self, parent=None):
super(Tool2, self).__init__(parent)
layout = QtGui.QGridLayout()
layout.addWidget(QtGui.QTextEdit('Tool #2'))
self.setLayout(layout)
self.hide()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
instance = myApplication()
instance.show()
sys.exit(app.exec_())
Which results in:
This is a follow-up to my unanswered question here. The code, exactly as it is below crashes with a segfault (copy/paste it into your system and run it). When I remove type=QtCore.Qt.DirectConnection from one or both of the signal constructor calls (thereby using QtCore.Qt.AutoConnection instead), everything runs the way it should: A widget appears, showing five progress bars filling up then emptying in an endless loop.
from PySide import QtCore, QtGui
import time
class Worker(QtCore.QThread):
sig_worker_update_progress = QtCore.Signal(int, int)
def __init__(self, thread_id, *args, **kwargs):
super(Worker, self).__init__(*args, **kwargs)
self.thread_id = thread_id
self.stop_requested = False
def slot_interrupt(self):
self.stop_requested = True
def run(self):
progress = 0
while(True):
self.sig_worker_update_progress.emit(self.thread_id, progress % 100)
progress += 1
if self.stop_requested:
break
else:
time.sleep(0.1)
class Controller(QtCore.QObject):
sig_controller_update_progress = QtCore.Signal(int, int)
def __init__(self, num_workers, *args, **kwargs):
super(Controller, self).__init__(*args, **kwargs)
self.workers = []
for i in range(num_workers):
self.workers.append(Worker(i))
self.workers[i].sig_worker_update_progress.connect(
self.slot_worker_update_progress,
type=QtCore.Qt.DirectConnection)
for worker in self.workers:
worker.start()
def slot_worker_update_progress(self, thread_id, progress):
# Do
# Stuff
self.sig_controller_update_progress.emit(thread_id, progress)
class Monitor(QtGui.QWidget):
def __init__(self, num_workers, *args, **kwargs):
super(Monitor, self).__init__(*args, **kwargs)
main_layout = QtGui.QVBoxLayout()
self.setLayout(main_layout)
self.progress_bars = []
for _ in range(num_workers):
progress_bar = QtGui.QProgressBar()
main_layout.addWidget(progress_bar)
self.progress_bars.append(progress_bar)
self.controller = Controller(num_workers)
self.controller.sig_controller_update_progress.connect(
self.slot_controller_update_progress,
type=QtCore.Qt.DirectConnection)
def slot_controller_update_progress(self, thread_id, progress):
self.progress_bars[thread_id].setValue(progress)
if __name__ == "__main__":
app = QtGui.QApplication([])
monitor = Monitor(5)
monitor.show()
app.exec_()
Why does using two nested DirectConnection signals cause a segfault? If Qt does not want you to do that, why isn't a more informative error given?
I am using PySide v1.2.2 which wraps the Qt 4.8 framework.
I found a satisfying explanation here. Apparently, emitting a signal of type DirectConnection is equivalent to a direct function call. So the GUI is after all updated on a Worker thread when both signals are DirectConnect-ed. As mentioned on my other question, threads are not allowed to change the GUI. The issue is NOT with nesting DirectConnections per se.
I am currently learning how to work with the tkinter gui and I am trying to integrate with SQLAlchemy. The first hurdle was figuring out how to move between frames without destroying/closing the window, luckily I found this question: Switch between two frames in tkinter. So I used a lot of the provided code and created a simple application that is trying to have someone log in.
Now for the part I can't figure out. I have a function _check_credentials(self) deep inside the structure of windows that is trying to query the db. I have successfully created the table and queried it elsewhere.
I need to pass the session from the main.py module to the Login class and finally to the _check_credentials(self) method.
main.py -> Authorzation() -> Login() -> login_btn = tk.Button() -> _check_credentials()
Additional info:
I have included all of my code. Right now it runs, but crashes when the login button is pressed.
I have attempted to pass the session directly, ie. Authorzation(Session) but that crashes immediately.
Also, I am trying to follow the guidelines laid out in the sqlalchemy docs http://docs.sqlalchemy.org/en/latest/orm/session.html, FAQ, "When do I construct a Session, when do I commit it, and when do I close it?".
main.py:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from module1 import Authorzation
import Tkinter as tk
Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
app = Authorzation()
app.mainloop()
module1.py:
import Tkinter as tk
import user
TITLE_FONT = ("Helvetica", 18, "bold")
class Authorzation(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (Login, Register):
frame = F(container, self)
self.frames[F] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Login)
def show_frame(self, c):
'''Show a frame for the given class'''
frame = self.frames[c]
frame.tkraise()
class Login(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Title", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
#Create the Username field
label_username = tk.Label(self, text="username")
label_username.pack()
self.username = tk.StringVar()
tk.Entry(self, textvariable=self.username).pack()
#Create the password field
label_password = tk.Label(self, text="password")
label_password.pack()
self.password = tk.StringVar()
tk.Entry(self, textvariable=self.password).pack()
login_btn = tk.Button(self, text="Login",
command=self._check_credentials)
login_btn.pack(pady=5)
reg_btn = tk.Button(self, text="Registration",
command=lambda: controller.show_frame(Register))
reg_btn.pack(pady=10)
def _check_credentials(self):
session_user = session.query(user.User)\
.filter_by(
username=self.username
).first()
if session_user:
return session_user.check_pwd(), session_user
else:
print("Sorry we could not find your username")
return False, None
self.controller.show_frame(Login)
class Register(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="This is page 1", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame(Login))
button.pack()
After doing research on *args, and **kwargs. I realized I had not tried putting the session first in the __init__() function. Here is how I then passed the session to the controller.
main.py:
app = Authorzation(session)
module1.py
class Authorzation(tk.Tk):
def __init__(self, session, *args, **kwargs):
self.session = session
Once the session was in the controller it was easy to reference it from the Login() frame.
module1.py:
class Login(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
...
def _check_credentials(self):
session = self.controller.session
Note: this solution is also nice because it alleviates the need to pass the session through the button.