Python Forum
[PyQt] No reaction and no error message when clicking button
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyQt] No reaction and no error message when clicking button
#1
Hi

I have no idea why nothing happens after clicking on "Start Test" in the given Python 3 (PyQt5) code. There appears also no error message.

mainwindow.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>350</width>
    <height>250</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Color Concentration Test</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout_2">
    <item row="0" column="0">
     <layout class="QVBoxLayout" name="verticalLayout">
      <item>
       <spacer name="verticalSpacer_2">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>40</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QGroupBox" name="">
        <layout class="QGridLayout" name="gridLayout">
         <item row="1" column="1">
          <widget class="QSpinBox" name="spinBox_switch_time">
           <property name="maximum">
            <number>100000</number>
           </property>
           <property name="value">
            <number>1000</number>
           </property>
          </widget>
         </item>
         <item row="1" column="0">
          <widget class="QLabel" name="label_switch_time">
           <property name="text">
            <string>Switch Time (ms):</string>
           </property>
           <property name="alignment">
            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
         <item row="0" column="1">
          <widget class="QComboBox" name="comboBox_designated_color">
           <property name="currentText">
            <string/>
           </property>
          </widget>
         </item>
         <item row="0" column="0">
          <widget class="QLabel" name="label_designated_color">
           <property name="text">
            <string>Designated Color:</string>
           </property>
           <property name="alignment">
            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
        </layout>
       </widget>
      </item>
      <item>
       <spacer name="verticalSpacer">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>40</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="pushButton_start_test">
        <property name="layoutDirection">
         <enum>Qt::LeftToRight</enum>
        </property>
        <property name="text">
         <string>Start Test</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>350</width>
     <height>28</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>
main.py:
import sys
import time
from random import choice

from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi
from PyQt5 import QtCore


class ConcentrationTest(object):
    def __init__(self, COLORS, designated_color):
        self.COLORS = COLORS
        self.designated_color = designated_color
        self.start_time = None
        self.color = None
        self.color_locked = True
        self.reaction_times = []
        self.false_positives = 0
        self.color_missed = 0
        self.key_too_often_pressed = 0

    def space_pressed(self):
        if self.color == self.designated_color:
            if self.color_locked:
                self.key_too_often_pressed += 1
            else:
                reaction_time = time.monotonic() - self.start_time
                self.reaction_times.append(reaction_time)
                self.color_locked = True
        else:
            self.false_positives += 1

    def next_round(self):
        if not self.color_locked and self.color == self.designated_color:
            self.color_missed += 1
        self.color_locked = False
        current_color = self.color
        while self.color == current_color:
            self.color = choice(list(self.COLORS.values()))
        self.start_time = time.monotonic()
        return self.color


class MainWindow(QMainWindow):
    COLORS = {"Black": QtCore.Qt.black,
        "Blue": QtCore.Qt.blue,
        "Cyan": QtCore.Qt.cyan,
        "Green": QtCore.Qt.green,
        "Magenta": QtCore.Qt.magenta,
        "Red": QtCore.Qt.red,
        "Yellow": QtCore.Qt.yellow}

    def __init__(self, concentration_test, parent=None):
        super().__init__(parent)
        loadUi("mainwindow.ui", self)
        self.concentration_test = concentration_test
        self.comboBox_designated_color.addItems(self.COLORS)
        self.pushButton_start_test.clicked.connect(self.show_concentration_test_window)

    def get_designated_color(self):
        return self.COLORS[self.comboBox_designated_color.currentText()]

    def get_switch_time(self):
        return self.spinBox_switch_time.value()

    def show_concentration_test_window(self):
        concentration_test = ConcentrationTest(self.COLORS, self.get_designated_color())
        concentration_test_window = ConcentrationTestWindow(concentration_test, self.get_switch_time())
        concentration_test_window.show()
        concentration_test_window.change_screen_color()


class ConcentrationTestWindow(QMainWindow):
    def __init__(self, concentration_test, switch_time, parent=None):
        super().__init__(parent)
        self.showFullScreen()
        self.concentration_test = concentration_test
        self.switch_time = switch_time
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.change_screen_color)
        self.pal = self.palette()
        self.setAutoFillBackground(True)

    def change_screen_color(self):
        color = self.concentration_test.next_round()
        self.pal.setColor(self.backgroundRole(), color)
        self.setPalette(self.pal)
        self.timer.start(self.switch_time)

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.close()

        if event.key() == QtCore.Qt.Key_Space:
            self.concentration_test.space_pressed()



def main():
    app = QApplication(sys.argv)
    main_window = MainWindow(app)
    main_window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
What is the reason for this?
Reply
#2
The problem is that your subwindow must be owned by another object, else it is immediatly destroyed by the garbage collector. Therefore, this works (see the 'show_concentration_test_window' method):

#!/usr/bin/python3
import sys
import time
from random import choice
 
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi
from PyQt5 import QtCore
 
 
class ConcentrationTest(object):
    def __init__(self, COLORS, designated_color):
        self.COLORS = COLORS
        self.designated_color = designated_color
        self.start_time = None
        self.color = None
        self.color_locked = True
        self.reaction_times = []
        self.false_positives = 0
        self.color_missed = 0
        self.key_too_often_pressed = 0
 
    def space_pressed(self):
        if self.color == self.designated_color:
            if self.color_locked:
                self.key_too_often_pressed += 1
            else:
                reaction_time = time.monotonic() - self.start_time
                self.reaction_times.append(reaction_time)
                self.color_locked = True
        else:
            self.false_positives += 1
 
    def next_round(self):
        if not self.color_locked and self.color == self.designated_color:
            self.color_missed += 1
        self.color_locked = False
        current_color = self.color
        while self.color == current_color:
            self.color = choice(list(self.COLORS.values()))
        self.start_time = time.monotonic()
        return self.color
 
 
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__()
        loadUi("mainwindow.ui", self)
        self.COLORS = {"Black": QtCore.Qt.black,
            "Blue": QtCore.Qt.blue,
            "Cyan": QtCore.Qt.cyan,
            "Green": QtCore.Qt.green,
            "Magenta": QtCore.Qt.magenta,
            "Red": QtCore.Qt.red,
            "Yellow": QtCore.Qt.yellow}
        self.comboBox_designated_color.addItems(self.COLORS)
        self.pushButton_start_test.clicked.connect(self.show_concentration_test_window)
 
    def get_designated_color(self):
        return self.COLORS[self.comboBox_designated_color.currentText()]
 
    def get_switch_time(self):
        return self.spinBox_switch_time.value()
 
    def show_concentration_test_window(self):
        self.concentration_test = ConcentrationTest(self.COLORS, self.get_designated_color())
        self.concentration_test_window = ConcentrationTestWindow(self.concentration_test, self.get_switch_time())
        self.concentration_test_window.show()
        self.concentration_test_window.change_screen_color()
 
 
class ConcentrationTestWindow(QMainWindow):
    def __init__(self, concentration_test, switch_time, parent=None):
        super().__init__(parent)
        self.showFullScreen()
        self.concentration_test = concentration_test
        self.switch_time = switch_time
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.change_screen_color)
        self.pal = self.palette()
        self.setAutoFillBackground(True)
 
    def change_screen_color(self):
        color = self.concentration_test.next_round()
        self.pal.setColor(self.backgroundRole(), color)
        self.setPalette(self.pal)
        self.timer.start(self.switch_time)
 
    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.close()
 
        if event.key() == QtCore.Qt.Key_Space:
            self.concentration_test.space_pressed()
 
 
 
def main():
    app = QApplication(sys.argv)
    main_window = MainWindow(app)
    main_window.show()
    sys.exit(app.exec_())
 
 
if __name__ == "__main__":
    main()
From a quick look, the structure would also need to be reviewed a bit, i.e. why declare concentration_test here
def __init__(self, concentration_test, parent=None):
as when you init the MainWindow, only app is passed
    main_window = MainWindow(app)
Reply
#3
Many thanks for fixing the code. I have some questions about your changes:

1. Does your explanation mean that every window instantiated after the main window always needs to be an attribute of another object?


'show_concentration_test_window' method before your fixes:
    def show_concentration_test_window(self):
        concentration_test = ConcentrationTest(self.COLORS, self.get_designated_color())
        concentration_test_window = ConcentrationTestWindow(concentration_test, self.get_switch_time())
        concentration_test_window.show()
        concentration_test_window.change_screen_color()
'show_concentration_test_window' method after your fixes:
    def show_concentration_test_window(self):
        self.concentration_test = ConcentrationTest(self.COLORS, self.get_designated_color())
        self.concentration_test_window = ConcentrationTestWindow(self.concentration_test, self.get_switch_time())
        self.concentration_test_window.show()
        self.concentration_test_window.change_screen_color()


'__init__' method of 'MainWindow' class before your fixes:
class MainWindow(QMainWindow):
    COLORS = {"Black": QtCore.Qt.black,
              "Blue": QtCore.Qt.blue,
              "Cyan": QtCore.Qt.cyan,
              "Green": QtCore.Qt.green,
              "Magenta": QtCore.Qt.magenta,
              "Red": QtCore.Qt.red,
              "Yellow": QtCore.Qt.yellow}

    def __init__(self, concentration_test, parent=None):
        super().__init__(parent)
        loadUi("mainwindow.ui", self)
        self.concentration_test = concentration_test
        self.comboBox_designated_color.addItems(self.COLORS)
        self.pushButton_start_test.clicked.connect(self.show_concentration_test_window)
The declaration of 'concentration_test' seems to be a relic from last refactoring.

'__init__' method of 'MainWindow' class after your fixes:
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__()
        loadUi("mainwindow.ui", self)
        self.COLORS = {"Black": QtCore.Qt.black,
                       "Blue": QtCore.Qt.blue,
                       "Cyan": QtCore.Qt.cyan,
                       "Green": QtCore.Qt.green,
                       "Magenta": QtCore.Qt.magenta,
                       "Red": QtCore.Qt.red,
                       "Yellow": QtCore.Qt.yellow}
        self.comboBox_designated_color.addItems(self.COLORS)
        self.pushButton_start_test.clicked.connect(self.show_concentration_test_window)


2. What's the reason for changing the (constant) class variable 'COLORS' to an object variable 'self.COLORS'?
Reply
#4
(Nov-22-2018, 03:19 PM)Atalanttore Wrote: 1. Does your explanation mean that every window instantiated after the main window always needs to be an attribute of another object?

I think that when you have a blocking window, such as:

        msg = QtWidgets.QInputDialog()
        print(msg.exec_())
You don't need to attribute it to self. In your case, the show() method was not blocking, so the new window is destroyed right after the execution of the method (I could see the window appearing for a fraction of a sec).

(Nov-22-2018, 03:19 PM)Atalanttore Wrote: 2. What's the reason for changing the (constant) class variable 'COLORS' to an object variable 'self.COLORS'?

I just felt that it was weird to see a variable declared there. But to avoid clutter in the top of your class I would rather save this space for declarations that requires it (Qt macros or thread signals). I'm not sure about what the best practice, so it might just be a matter of personnal preferences but I think it make more sense this way.
Reply
#5
Thanks for your explanation.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] Button error Tyrel 2 213 Jun-20-2021, 07:21 AM
Last Post: Tyrel
  Error message box and quit app Kumarkv 1 1,110 May-19-2020, 07:05 PM
Last Post: Larz60+
  [Tkinter] How to make message box error stay on top of window scratchmyhead 1 1,065 May-10-2020, 10:21 PM
Last Post: scratchmyhead
  Need tkinter help with clicking buttons pythonprogrammer 2 850 Jan-03-2020, 04:43 AM
Last Post: joe_momma
  [PySimpleGui] How to alter mouse click button of a standard submit button? skyerosebud 3 2,211 Jul-21-2019, 06:02 PM
Last Post: FullOfHelp
  [Tkinter] RE: status bar to return to the centre after 1 minute of clicking a button ? chano 6 1,842 May-27-2019, 04:24 PM
Last Post: Yoriz
  tkinter- adding a new window after clicking a button built on the gui ShashankDS 2 4,265 Apr-18-2019, 12:48 PM
Last Post: ShashankDS
  [Tkinter] How to get & delete details from each input by clicking a button Vicolas 6 1,481 Feb-01-2019, 11:00 AM
Last Post: Vicolas
  [PyQt] Close program using Push Button with the help of input from a Message Box bhargavbn 2 3,500 Oct-30-2018, 05:09 AM
Last Post: bhargavbn
  [Tkinter] Close Toplevel after clicking button jollydragon 3 7,393 Jul-12-2018, 06:22 AM
Last Post: jollydragon

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020