How to Add a User Interrupt to an Infinite Loop

How can I add a user interrupt to an infinite loop?

I think you will have to check the exit condition in a separate thread:

# check for exit condition
Thread.new do
loop do
exit if gets.chomp == 'q'
end
end

a = 0
loop do
a += 1
puts a
sleep 1
end

BTW, you will have to enter q<Enter> to exit, as that's how standard input works.

How to interrupt an Infinite Loop

I feel dirty even writing this, but...

From a different thread, you could call System.setOut() with a PrintStream implementation, which throws a RuntimeException when you call println().

Interrupt Python infinite while loop with key press

A way to clean up the use of CTRL-C is to use try-except to catch the KeyboardInterrupt like:

try:
while True:
...
...
except KeyboardInterrupt:
exit

User input to break infinite loop?

This is what exceptions are can be used for: you press Ctrl+C to interrupt the process, and your code handles it by saving the results:

while True:
try:
# your code here
except KeyboardInterrupt:
go(dict)
break

Note that you can't return from a standalone loop, but you can break from it, however.

Avoid an infinite loop while waiting for user input?

There's no need to do any explicit looping. The event loop already does it for you. You execute actions when certain events happen, e.g. when a button is checked or unchecked.

It'd help to factor out the controller from the UI, and formally specify its behavior using a UML statechart. The code below corresponds 1:1 to the statechart.


Statechart of the Controller

The s_moving composite state has no initial state since it's never entered directly, only implicitly when entering its substates.

// https://github.com/KubaO/stackoverflown/tree/master/questions/wiringpi-isr-38740702
#include <QtWidgets>
#include <wiringpi.h>

class Controller : public QObject {
Q_OBJECT
QStateMachine m_mach{this};
QState s_stopped{&m_mach};
QState s_moving {&m_mach};
QState s_forward{&s_moving};
QState s_reverse{&s_moving};
static QPointer<Controller> m_instance;
enum { sensorPin = 24, motorPinA = 10, motorPinB = 11 };
// These methods use digitalWrite() to control the motor
static void motorForward() {
digitalWrite(motorPinA, HIGH);
digitalWrite(motorPinB, LOW);
}
static void motorReverse() { /*...*/ }
static void motorStop() { /*...*/ }
//
Q_SIGNAL void toStopped();
Q_SIGNAL void toForward();
Q_SIGNAL void toReverse();
void setupIO() {
wiringPiSetupSys();
pinMode(sensorPin, INPUT);
wiringPiISR(sensorPin, INT_EDGE_RISING, &Controller::sensorHit);
}

The sensorHit() interrupt handler is called by the wiringPi library from a high-priority worker thread that waits for GPIO transitions as reported by the kernel. To minimize the latency of reversing the motor, we leverage this thread. Since sensorHit() already runs in a high-priority thread and is as close to the GPIO transition as possible, we immediately set the reverse motor direction, and emit a signal to instruct the state machine to transition to the s_reverse state. Since this signal is emitted from a thread different than the one the main thread the Controller instance lives in, the slot call is queued in the main thread's event queue.

   /// This method is safe to be called from any thread.
static void sensorHit() {
motorReverse(); // do it right away in the high-priority thread
emit m_instance->toReverse();
}
public:
Controller(QObject * parent = nullptr) : QObject{parent} {
Q_ASSERT(!m_instance);
// State Machine Definition
m_mach.setInitialState(&s_stopped);
s_stopped.addTransition(this, &Controller::toForward, &s_forward);
s_moving.addTransition (this, &Controller::toStopped, &s_stopped);
s_forward.addTransition(this, &Controller::toReverse, &s_reverse);
s_reverse.addTransition(this, &Controller::toForward, &s_forward);
connect(&s_stopped, &QState::entered, this, [this]{
motorStop();
emit isStopped();
});
connect(&s_forward, &QState::entered, this, [this]{
motorForward();
emit isForward();
});
connect(&s_reverse, &QState::entered, this, [this]{
motorReverse();
emit isReverse();
});
m_mach.start();
//
m_instance = this;
setupIO();
}
Q_SLOT void forward() { emit toForward(); }
Q_SLOT void stop() {
motorStop(); // do it right away to ensure we stop ASAP
emit toStopped();
}
Q_SIGNAL void isStopped();
Q_SIGNAL void isForward();
Q_SIGNAL void isReverse();
};
QPointer<Controller> Controller::m_instance;

The UI is decoupled from the controller: neither UI nor controller objects are directly aware of each other until you link them using connect:

int main(int argc, char ** argv) {
using Q = QObject;
QApplication app{argc, argv};
Controller ctl;
QWidget ui;
QVBoxLayout layout{&ui};
QLabel state;
QPushButton move{"Move Forward"};
QPushButton stop{"Stop"};
layout.addWidget(&state);
layout.addWidget(&move);
layout.addWidget(&stop);
Q::connect(&ctl, &Controller::isStopped, &state, [&]{ state.setText("Stopped"); });
Q::connect(&ctl, &Controller::isForward, &state, [&]{ state.setText("Forward"); });
Q::connect(&ctl, &Controller::isReverse, &state, [&]{ state.setText("Reverse"); });
Q::connect(&move, &QPushButton::clicked, &ctl, &Controller::forward);
Q::connect(&stop, &QPushButton::clicked, &ctl, &Controller::stop);
ui.show();
return app.exec();
}

#include "main.moc"

To facilitate testing on desktop platforms, we can add a trivial WiringPi mockup to make it all self-contained:

// A rather silly WiringPi mockup
std::function<void()> isr;
int wiringPiSetupSys() { return 0; }
void pinMode(int, int) {}
void digitalWrite(int pin, int value) {
if (pin == 10 && value == HIGH)
QTimer::singleShot(1000, isr);
}
int wiringPiISR(int, int, void (*function)()) {
isr = function;
return 0;
}

Ending an infinite while loop

You can try wrapping that code in a try/except block, because keyboard interrupts are just exceptions:

try:
while True:
IDs2=UpdatePoints(value,IDs2)
time.sleep(10)
except KeyboardInterrupt:
print('interrupted!')

Then you can exit the loop with CTRL-C.

How do I interrupt a infinite loop without using raw_input or Ctrl-C? Python 2.7

This is an interesting and surprisingly complicated problem (not sure why the downvotes...) You have to bypass the standard "read till the end of line" as well as add a timeout on normally blocking read. Here is my answer (which works only on linux/mac but see the links on ideas of extending it to Windows):

import select
import sys, termios

def getchar():
char = '_'
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~(termios.ECHO | termios.ICANON) # turn off echo and canonical mode which sends data on delimiters (new line or OEF, etc)
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new) # terminal is now
ready, steady, go = select.select([sys.stdin], [], [], 1)
if ready:
char = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
return char

try:
while True:
print "I'm looping!"
c = getchar()
if c in ' \n':
print "The loop has ended!"
break
except KeyboardInterrupt:
print "The loop has ended!"

It is a combination of this answer and this answer. Apparently there is also readchar library as suggested by this answer.



Related Topics



Leave a reply



Submit