Python garbage collection and gtk windows - python

In the following program I have a button that spawns a popup. Simple enough. Now I connect the main window's delete-event to Gtk.main_quit() so that closing the main window closes the program.
Without that it will keep running until I kill the process (As evidenced by the occupied CLI prompt) The question then is: What happens to the popup window when I click it away?
Is the window itself automatically being destroyed at the delete-event or does it just hide itself and linger somewhere in memory until the program ends?
#!/usr/bin/python3
from gi.repository import Gtk
class MainWin(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
button = PopupButton()
self.add(button)
self.show_all();
self.connect("delete-event", Gtk.main_quit)
class PopupButton(Gtk.Button):
def __init__(self):
Gtk.Button.__init__(self, label="Popup")
self.connect("clicked", self.clicked)
def clicked(self, widget):
win = PopupWindow()
win.set_transient_for(self.get_toplevel())
win.show()
class PopupWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.add(Gtk.Label(label="Popups! Popups for everyone!"))
self.show_all()
win = MainWin()
win.show()
Gtk.main()

The default response to the delete-event signal is to destroy the window. So, unless you're handling that signal, the popup window is destroyed.

Related

How to Pygtk WindowTypeHint to DOCK on XFCE and have it visible on the desktop

So I've run into an issue where whenever I set the WindowTypeHint to anything other than normal the window just disappears. I've verified the hint type with print.Below is my sample code
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
# Create the main window
class MyWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Hello World")
# Innitially setting the bar to off
self.set_decorated(False)
# Attempting to set window type to prevent minimizing when show desktop is hit and to stay behind other objects etc.
self.set_type_hint(Gdk.WindowTypeHint.DOCK)
# Defining the button and drawing it
self.button = Gtk.Button(label="Click Here")
self.button.connect("clicked", self.on_button_clicked)
self.add(self.button)
# define the button functions
def on_button_clicked(self, widget):
if win.props.decorated == False:
win.set_decorated(True)
else:
win.set_decorated(False)
print("Hello World")
print(win.props.decorated)
print(self.props.type_hint)
# Set alias for the window
win = MyWindow()
# Testing. Print the hint type to console
print(win.props.type_hint)
# Window settings.
win.set_keep_below(True)
win.connect("destroy", Gtk.main_quit)
win.show_all()
print(win.props.type_hint)
Gtk.main()
Window to appear as a dock on the desktop where the mouse pointer is or in the corner of one of the monitors the same way glava works with it's settings.
Item was drawing off screen. Multiple monitors had it throwing the item into the corner off screen between them. If anyone else comes across this I resolved it by setting the coordinates with self.move(x, y) in the initialisation. Found where I wanted it to root with win.get_position() on button press to find where I wanted it.

How to quit Qt application before main window is displayed

On launch, my Qt application displays a dialog box with some options, before displaying the main window. The dialog box has a 'Start' and 'Cancel' button. The same dialog box is used at a later time, after the main window is displayed, when the user clicks on a 'New Game' button.
I'm trying to have the 'Cancel' button quit the application if it's the only interface element being displayed (i.e. on application launch). In my current code, however, the main window is still displayed.
If I replace self.view.destroy() with self.view.deleteLater() the main window briefly flashes into existence before disappearing (and quitting properly), but this can't be the solution.
If I move the view.show() call inside the dialog.exec_() block, it doesn't work either. Mainly because I would be calling view.show() every time the dialog is displayed again from within the main window, but also because even in this case the app doesn't really quit. The main window, in this case, is not displayed but the process is keeps running (Python application icon still visible in the Dock).
What am I doing wrong here? I've read other similar questions but I don't understand how to apply these solutions in my case.
(PySide2 5.15.1 on macOS 10.15.6)
class App:
def __init__(self, app, game, view):
self.app = app
self.game = game
self.view = view
# Display the dialog
# Same callback is bound to a QPushButton in MainWindow
self.cb_start_dialog()
def cb_start_dialog(self):
# Do some initialisation
dialog = DialogNewGame() # A subclass of QDialog
if dialog.exec_():
# Setup the interface
else:
if not self.view.isVisible():
# Condition evaluates correctly
# (False on app launch,
# True if main window is displayed)
self.view.destroy()
self.app.quit() # Doesn't work, main window still displayed
def main():
application = QApplication()
view = MainWindow() # A QWidget with the main window
model = Game() # Application logic
App(application, model, view)
view.show()
application.exec_()
If the code is analyzed well, it is observed that "quit" is invoked before the eventloop starts, so it makes no sense to terminate an eventloop that never started. The solution is to invoke X an instant after the eventloop starts. On the other hand, the quit method is static so it is not necessary to access "self.app"
from PySide2.QtCore import QTimer
from PySide2.QtWidgets import QApplication, QDialog, QMainWindow
class MainWindow(QMainWindow):
pass
class DialogNewGame(QDialog):
pass
class Game:
pass
class App:
def __init__(self, game, view):
self.game = game
self.view = view
QTimer.singleShot(0, self.cb_start_dialog)
def cb_start_dialog(self):
dialog = DialogNewGame()
if dialog.exec_():
pass
else:
QApplication.quit()
def main():
application = QApplication()
view = MainWindow()
model = Game()
app = App(model, view)
view.show()
application.exec_()
if __name__ == "__main__":
main()

wxPython in thread can not be shut down without error

I am new to wxPython, so I basically just copied something that will display a tray icon and made it a thread:
import wx
import wx.adv
from threading import Thread
TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = 'icon.png'
class Main(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
self.app = App()
self.app.MainLoop()
def create_menu_item(menu, label, func):
item = wx.MenuItem(menu, -1, label)
menu.Bind(wx.EVT_MENU, func, id=item.GetId())
menu.Append(item)
return item
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self, frame):
self.frame = frame
super(TaskBarIcon, self).__init__()
self.set_icon(TRAY_ICON)
self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)
def CreatePopupMenu(self):
menu = wx.Menu()
create_menu_item(menu, 'Exit', self.on_exit)
return menu
def set_icon(self, path):
icon = wx.Icon(wx.Bitmap(path))
self.SetIcon(icon, TRAY_TOOLTIP)
def on_left_down(self, event):
print('Tray icon was left-clicked.')
def on_exit(self, event):
wx.CallAfter(self.Destroy)
self.frame.Close()
class App(wx.App):
def __init__(self):
wx.App.__init__(self, False)
def OnInit(self):
frame = wx.Frame(None)
self.SetTopWindow(frame)
TaskBarIcon(frame)
return True
from my main thread, which is a very long running service, I am starting the GUI thread, activating the scheduler and checking if it should run like so:
gui = trayIcon.Main()
gui.start()
schedule.every(60).minutes.do(main)
while gui.is_alive():
schedule.run_pending()
time.sleep(1)
# if this is reached the gui thread has terminated and the program should shut down
sys.exit()
It works as it should be: When the exit item in the tray icon menu is clicked, the GUI thread shuts down, is then no longer detected in the while loop of the main thread and sys.exit() is called.
Unfortunately, wxPython then shows an error dialog with the following text:
wxWidgets Debug Alert
....\src\common\socket.cpp(767): assert "wxIsMainThread()" failed in
wxSocketBase::IsInitialized(): unsafe to call from other threads [in
thread 1284] Do you want to stop the program? You can also choose
[Cancel] to suppress further warnings.
How can I shut down the GUI correctly or at least suppress this warning? After it the program quits as it should be, although I suspect suppressing the message would leave a memory leak of some kind.
Thanks in advance
Taxel
Basically it looks like you have set it up backwards. The main thread should always be the wxPython one. MainLoop should not be called inside a thread. Instead you should let wxPython be in control and run your long running thread inside of it.
Then when your long running task is finished, you can use a thread-safe method, like wx.CallAfter to tell wxPython that it is time to exit. Since wxPython is the one in control, it can close itself correctly.
Here are some articles that might help you:
https://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
https://wiki.wxpython.org/LongRunningTasks

python gtk multi window

i have a big problem (for me!) with python gtk module.
i can open multi windows; but i can't close singly ( one time , one window ).
if i close a window, all windows close automatically.
i want to close the first window only. after closing firt window, come a new window ( by my choice).
for example :
#!/usr/bin/env python
import pygtk
pygtk.require20()
import gtk
class CLS1(object):
def __init__(self):
self.mywindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.mywindow.connect("delete_event", gtk.main_quit)
self.btn = gtk.Button("Cls1|Btn")
self.mywindow.add(self.btn)
self.mywindow.show_all()
def main(self):
gtk.main()
class CLS2(object):
def __init__(self):
self.mywindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.mywindow.connect("delete_event", gtk.main_quit)
self.btn = gtk.Button("Cls2|Btn")
self.mywindow.add(self.btn)
self.mywindow.show_all()
def main(self):
gtk.main()
class APP(object):
def __init__(self):
self.mywindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.mywindow.connect("delete_event", gtk.main_quit)
self.hori = gtk.HBox()
self.btn1 = gtk.Button("AppBtn1")
self.btn2 = gtk.Button("AppBtn2")
self.btn1.connect("clicked", self.show_me , "AppBtn1")
self.btn2.connect("clicked", self.show_me , "AppBtn2")
self.hori.pack_start(self.btn1)
self.hori.pack_start(self.btn2)
self.mywindow.add(self.hori)
self.mywindow.show_all()
def show_me(self, penar, data):
if data=="AppBtn1" :
CLS1().main()
if data=="AppBtn2":
CLS2().main()
gtk.main_quit()
def main(self):
gtk.main()
APP().main()
i want this :
1- i will run the program
2- the "APP" class will work and will come "APP" window
3- if i click a button (AppBt1 or AppBtn2) ; the "APP" window will close (automatically ; not by me!)
4- if i was clicked "AppBtn1" button on "APP" window (#step 3) ; the "CLS1" class will work and its window will open
,or if i was clicked "AppBtn2" button on "APP" window (#step 3) ; the "CLS2" class will work and its window will open
i wanna only one window on the screen while program running; if i click a button ; its window will close and a new window will open (by my choice, and automatically!)
how can i do this? and can you write its code :)
thanks a lot !
Calling gtk.main_quit will kill the whole program (it basically stop GTK). So what you need, is just to stop GTK when the last window has been closed. What you're currently doing is stopping GTK when any window is closed.
So just use a global variable that you will use as a counter of the windows open. On the delete-event handler, decrement that counter, and if it reached 0 that means you have no more windows open, and you can call gtk.main_quit, otherwise, do nothing and the window will just be normally destroyed.
To kill the parent window, just pass the parent as the last parameter when you connect to the clicked signal. In the associated callback, you'll get that last parameter and call gtk.Widget.destroy on it.
Well a better way might be to modify the window that's already open instead of closing it and opening another.

PyQt: Accesing Main Window's Data from a dialog?

So, I'm using Python and PyQt. I have a Main Window that contains a QTableWidget, and a dialog that opens modally and has some QLineEdit widgets... All right so far, but I have 2 problems:
When the dialog opens, my Main Window freezes, and I don't really like that...
What I want, when I finish editing a QLineEdit, is that the program will search the QTableWidget, and if the text from the QLineEdit exists in the table, a dialog will come up and informe about that. That's the general idea. But, so far, I seem to only be able to create a new QTableWidget instance, and I can't use the data from the existing...
What can I do about these?
You wrote:
and a dialog that opens modally
and then:
When the dialog opens, my Main Window freezes
The docs say:
int QDialog::exec () [slot]
Shows the dialog as a modal dialog,
blocking until the user closes it. The function returns a DialogCode
result. If the dialog is application modal, users cannot interact with
any other window in the same application until they close the dialog.
If the dialog is window modal, only interaction with the parent window
is blocked while the dialog is open. By default, the dialog is
application modal.
About modeless dialogs:
A modeless dialog is a dialog that operates independently of other
windows in the same application. Find and replace dialogs in
word-processors are often modeless to allow the user to interact with
both the application's main window and with the dialog.
Modeless
dialogs are displayed using show(), which returns control to the
caller immediately.
An example:
import sys
from PyQt4 import QtCore, QtGui
class SearchDialog(QtGui.QDialog):
def __init__(self, parent = None):
QtGui.QDialog.__init__(self, parent)
self.setWindowTitle('Search')
self.searchEdit = QtGui.QLineEdit()
layout = QtGui.QVBoxLayout()
layout.addWidget(self.searchEdit)
self.setLayout(layout)
class MainWindow(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self, None)
self.resize(QtCore.QSize(320, 240))
self.setWindowTitle('Main window')
self.logText = QtGui.QPlainTextEdit()
searchButton = QtGui.QPushButton('Search')
layout = QtGui.QVBoxLayout()
layout.addWidget(self.logText)
layout.addWidget(searchButton)
self.setLayout(layout)
searchButton.clicked.connect(self.showSearchDialog)
def showSearchDialog(self):
searchDialog = SearchDialog(self)
searchDialog.show()
searchDialog.searchEdit.returnPressed.connect(self.onSearch)
def onSearch(self):
self.logText.appendPlainText(self.sender().text())
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec_()
if __name__ == "__main__":
main()
Click 'Search' to open a search window (you can open several of them). Enter a text to search and press Enter. The text to search will be added to the log in the main window.

Categories