Why does my button crash klayout? This worked out for me in 0.28.7 but doesn't work in 0.28.17

edited March 27 in Python scripting

Setup Notes:
I'm on Windows 11

This crashes or closes main window?

I'm really bad at classes I don't really get the whole super or anything I'm just copying what I see other places.

The goal is to add a button to the toolbar, and when the user clicks it a form pops up that they can then use to interact with other elements and run other scripts. If the form is already up, that's fine my goal would be to just replace it with a new one. If the form is closed, then give me a new one. If there was a previously existing form that was closed and you click the button... then we should get a new one.

Basically click button no matter what the state of anything else, get the form.
Right now, my working example I think when you "close" the form with the "X" in the corner, it just hides? Or something because it doesn't stop existing apparently?

The issue happens when you open my window, then close the window, then open it again --> instant crash

Also if you just spam clicks on the open button, the window opens several times, then throw a runtime error, then you keep clicking and the window won't open.

This worked fine in
0.28.7 but crashes now in 0.28.17

I've included an example that works the way I want it to, although it seems janky :)

# THIS ONE CRASHES AND BEHAVES BADLY
from pya import *
import pya
import struct
from pathlib import Path
import subprocess
import datetime
import os
import time

class TestWidget(pya.QWidget):
    def __init__(self, parent = None):
        super(TestWidget, self).__init__()
        self.win = pya.QWidget()
        self.main_layout = pya.QVBoxLayout()
        self.a_btn = pya.QPushButton()
        self.main_layout.addWidget(self.a_btn)

        self.setWindowFlags(Qt.WindowStaysOnTopHint)        
        self.setLayout(self.main_layout)


def my_func_a():
    global w
    print("click")
    w = TestWidget(pya.Application.instance().main_window())
    w.show()
    w.resize(450,350)
    return w

a = pya.Action()
a.title="Open The dialog Box"
a.on_triggered(my_func_a)

#adds the button to launch on script run to the GUI
menu = pya.Application.instance().main_window().menu()
if '@toolbar.my_separator' not in menu.items("@toolbar"):
    menu.insert_separator("@toolbar.end", "my_separator")
menu.insert_item("@toolbar.end", "my_action", a)

This works, but I think it's not very elegant:

# THIS ONE WORKS BUT I FEEL LIKE IT IS JANKY
from pya import *
import pya
import struct
from pathlib import Path
import subprocess
import datetime
import os
import time


class TestWidget(pya.QWidget):
  def __init__(self, parent = None):
      super(TestWidget, self).__init__()
      self.win = pya.QWidget()
      self.main_layout = pya.QVBoxLayout()

      self.a_btn = pya.QPushButton()
      self.a_btn.clicked(self.print_a_msg)
      self.main_layout.addWidget(self.a_btn)

      #self.setWindowFlags(Qt.WindowStaysOnTopHint)
      #self.setAttribute(Qt.WA_DeleteOnClose)        
      self.setLayout(self.main_layout)
  def print_a_msg(self):
    print("You Clicked The Button!")


def my_func_a():
  global w
  print("opening the window")
  try:
    if w:
      print(f"w exists and is {type(w)}")
      #let_it_close = True
      w.show()
      w.activateWindow()
      #w._raise()
      #w.resize(450,350)
      return
  except RuntimeError as E:
    print(E)
    print("w doesn't exist")
    time.sleep(.125)
    w = TestWidget(pya.Application.instance().main_window())
    w.show()
    w.resize(450,350)
    return w
  except Exception as E:
    print(E)
    print("w doesn't exist")

  print("starting new w")
  w = TestWidget(pya.Application.instance().main_window())
  w.show()
  w.resize(450,350)
  return w

a = pya.Action()
a.title="Open The dialog Box"
a.on_triggered(my_func_a)

#adds the button to launch on script run to the GUI
menu = pya.Application.instance().main_window().menu()
if '@toolbar.my_separator' not in menu.items("@toolbar"):
  menu.insert_separator("@toolbar.end", "my_separator")
menu.insert_item("@toolbar.end", "my_action", a)

Comments

  • Hmm ..

    Thanks for the nicely prepared examples.

    However, I can't see the problem on Ubuntu :( I need to try on Windows. But I do not have Windows 11.

    There is basically nothing bad with the first code. When you re-assign w, the old window should go away and a new one is created.

    There are some bad interactions with the debugger when you run modal dialogs. This is because the debugger monitors calls in and out of the widget class and that involves Qt-internal events. This sometimes screws up things, for example painting. Did you try running with the IDE window closed?

    Matthias

  • Update: I tried with a fresh installation of 0.29.0 version on Windows 10 and no crash happens :(

    Matthias
  • edited April 3

    I actually did not try with the IDE closed. The only way to do that is to call the app with an arg?

    I tried with IDE open using normal "run" mode instead of "debug" mode but the behavior was the same. I'll try with version .29. Either way I've got a way forward. Thanks!

    Actually this makes total sense though because I'm launching 0.28.7 with an argument to the application embedded in a shortcut since it's my "completed" script. so it could totally be interaction with the IDE.

  • You can bind macros to menu items in the macro properties. This way, you can close the IDE and run the macro by selecting the menu. But if you disable debug, the effect should be the same.

    Having debug mode on is not a potential crash risk - which I try to minimize, but that is very hard when it comes to Qt code. It is very easy to trigger debugger activities from inside the debugger itself. That leads to very strange effects. A solution was to detach the debugger from the application process, but that is beyond the scope of that project.

    Anyway, I will try things when I get a grip on some Windows 11 installation. Maybe that makes a difference.

    Matthias

  • edited April 3

    @Matthias I tried running it without the IDE, same behavior using a shortcut that was loaded with this:
    Klayout_Instance\v0_28_17\klayout_app.exe -rm "Klayout_Instance\v0_28_17\debug_my_python_scripts\my_button_crashes.py"

    I've attached a gif of what I'm seeing. It looks like it repeats at first (I mean it's a gif it eventually will) but it's got 2 separate behaviors.

    First I show the app opens, I click the button and the form pops up. Then I close the form and click the button again and the app crashes.

    Then I re-launch the app, click the button, form opens, then I click the button again, form opens again once, but subsequent clicks throw a runtime error.

    I'll try V29 today.

    Edit: Doesn't crash in V29, seems to work fine in IDE :smile:

  • That's good news. I switched the Qt version in 0.29, maybe that was helping.

    Let's keep fingers crossed that it stays like this :)

    Matthias

Sign In or Register to comment.