How do I pause a loop with a button in PyGObject? - gtk

I have a button that runs a loop and updates the window accordingly. I want another "pause" button that pauses this loop but it doesn't seem like I can do that while the loop is running. Perhaps threading is the solution (I tried GLib.timeout_add_seconds without success) - what is the easiest way to make this work?
I have attached my faulty code:
import sys
from time import sleep
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class LoopButton(Gtk.Box):
def __init__(self, GUI):
Gtk.Box.__init__(self)
self.GUI = GUI
self.set_border_width(10)
self.message = "1"
button = Gtk.Button.new_with_label("Run")
button.connect("clicked", self.on_click)
self.pack_start(button, True, True, 0)
def on_click(self, widget):
msg = int(self.message)
while self.GUI.is_paused == False:
self.GUI.restart_window(str(msg))
msg += 1
while Gtk.events_pending():
Gtk.main_iteration()
sleep(1)
self.GUI.is_paused = True
class PauseButton(Gtk.Box):
def __init__(self, GUI):
Gtk.Box.__init__(self)
self.GUI = GUI
self.set_border_width(10)
button = Gtk.Button.new_with_label("Pause")
button.connect("clicked", self.on_click)
self.pack_start(button, True, True, 0)
def on_click(self, widget):
self.GUI.is_paused = True
class GUI:
def __init__(self):
self.is_paused = False
self.win = Gtk.Window()
self.window_grid = Gtk.Grid()
self.box = Gtk.Box(spacing=10)
self.label = Gtk.Label("Default label")
self.win.connect("delete-event", Gtk.main_quit)
self.start_window()
def start_window(self):
self.box.pack_start(LoopButton(self), True, True, 0)
self.box.pack_start(PauseButton(self), True, True, 0)
self.window_grid.add(self.box)
self.window_grid.add(self.label)
self.win.add(self.window_grid)
self.win.show_all()
def restart_window(self, label="Default label"):
self.window_grid.destroy()
self.window_grid = Gtk.Grid()
self.box = Gtk.Box(spacing=10)
self.label = Gtk.Label(label)
self.start_window()
def main():
app = GUI()
Gtk.main()
if __name__ == "__main__":
sys.exit(main())

Depending on what you really do in the loop there may be more necessary details, but below is an example of how looping is normally avoided in GLib/Gtk. The caveats of this method are: A) one iteration must be fast enough to not affect UI updates (a few milliseconds at most). B) The timing is not exact: the timeout function might not be called exactly 2 seconds after the previous call.
import gi
from gi.repository import GLib
class GUI:
def __init__(self):
# initialize the UI here...
# setup updates every two seconds
GLib.timeout_add_seconds(2, self.timeout_update)
def timeout_update(self):
# update your widgets here
print ("Updating UI now...")
# yes, we want to be called again
return True

Related

Kivy button press to next page

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.submit = Button(text='Order', font_size=40)
self.add_widget(self.submit)
self.submit.bind(on_press=self.pressed)
def pressed(self, instance):
pressing = Secondtab()
pressing()
class Secondtab(GridLayout):
def __init__(self, **kwargs):
super(Secondtab, self).__init__(**kwargs)
self.cols = 5
self.submit = Button(text='Drinks', font_size=40)
self.add_widget(self.submit)
print('check')
def __call__(self):
print('new tab')
class MyApp(App):
def build(self):
return LoginScreen()
if __name__ == '__main__':
MyApp().run()
So I want the window to change to a new layout when I press the first button. I tought making a new grid class and calling it would work but it didnt work as I expected. I dont want the button to open a new window tho I want the current window to refresh to the new layout.
As #John Anderson suggested. Take a look at the screen manager widget. The 'current window' must be a populated screen widget and you could change the current screen by assigning the next screen's name to the current attribute.
This will help you I guess. Using ScreenManager:
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.submit = Button(text='Order', font_size=40)
self.add_widget(self.submit)
self.submit.bind(on_press=self.pressed)
def pressed(self, instance):
pressing = Secondtab()
pressing()
myapp.screen_manager.transition = SlideTransition(direction='left', duration=.25) #You can change transition speed or you could just remove it to set it on default
myapp.screen_manager.current = 'Secondtab'
class MyApp(App):
def build(self):
return LoginScreen()
self.screen_manager = ScreenManager()
self.loginscreen = LoginScreen()
screen = Screen(name='LoginScreen')
screen.add_widget(self.loginscreen)
self.screen_manager.add_widget(screen)
self.secondtab = Secondtab()
screen = Screen(name='Secondtab')
screen.add_widget(self.secondtab)
self.screen_manager.add_widget(screen)
return self.screen_manager

Scalafx. Start the Alert by Timer

I have a main window with some information. I can't start the Alert by timer. I need to show alert every 10 seconds(with working main window) and by the alert's button change the Label's text in the main window.
I have this code, but it's not work:
object main extends JFXApp {
stage = new JFXApp.PrimaryStage() {
scene = new Scene {
val MyLabel = new Label("SomeText")
root = new VBox {
children = MyLabel
}
}
val ButtonTypeOne = new ButtonType("Change the text")
val ButtonTypeTwo = new ButtonType("No")
val alert1 = new Alert(AlertType.Warning) {
initOwner(stage)
title = "Warning!!!"
headerText = "Header"
contentText = "Do you need to change the text?"
buttonTypes = Seq(ButtonTypeOne, ButtonTypeTwo, ButtonType.Cancel)
}
val result = alert1.showAndWait()
val timerA = new PauseTransition(Duration(5000))
timerA.onFinished = { _ =>
result match {
case Some(ButtonTypeOne) => /*Here I need to change text in MyLabel */
case Some(ButtonTypeTwo) => None
case _ => None
}
timerA.playFromStart()
}
timerA.play
}
}
There are a few different problems in your code:
You display the alert only once, regardless of what's happening with the timer.
The code that reacts to the result of the alert in the timer always looks at the same initial result.
Unfortunately, even if you move the val result = alert1.showAndWait inside of the timer's animation update event, JavaFX makes it illegal to call showAndWait within such a routine.
The solution is to create an onHidden event handler for alert1, which reacts to it being closed. The button type used to close the dialog is then stored in alert1.result, so you can use that to determine what action to take.
I've added a StringProperty to assist with the changing of the label value. The following example should be a good starting point for what you want to achieve...
import scalafx.Includes._
import scalafx.animation.PauseTransition
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.beans.property.StringProperty
import scalafx.scene.Scene
import scalafx.scene.layout.VBox
import scalafx.scene.control.{Alert, ButtonType, Label}
import scalafx.scene.control.Alert.AlertType
import scalafx.util.Duration
object main extends JFXApp {
// String property for the text in myLabel.
val strProp = StringProperty("Some Text")
// Declare this so that it's accessible from timerA.onFinished.
val myLabel = new Label {
// Bind the text property to the value of strProp.
// When strProp's value changes, so does the label's text.
text <== strProp
}
stage = new PrimaryStage() {
scene = new Scene {
root = new VBox {
children = myLabel
}
}
}
// Custom buttons.
val ButtonTypeOne = new ButtonType("Change the text")
val ButtonTypeTwo = new ButtonType("No")
// Create the timer. This goes off after 5,000ms (5 seconds) - after play is called.
val timerA = new PauseTransition(Duration(5000))
// Alert dialog.
// Note: JavaFX forbids use of showAndWait within animation processing, so we must use
// an onHidden event instead.
val alert1 = new Alert(AlertType.Warning) {
initOwner(stage)
title = "Warning!!!"
headerText = "Header"
contentText = "Do you need to change the text?"
buttonTypes = Seq(ButtonTypeOne, ButtonTypeTwo, ButtonType.Cancel)
}
// React to the dialog being closed.
alert1.onHidden = {_ =>
alert1.result.value match {
// If button type one, change the property value.
// Note alert1.result.value is a JavaFX ButtonType, so use .delegate for the match.
case ButtonTypeOne.delegate => strProp.value = "Changed!"
// Otherwise, do nothing.
case _ =>
}
// Start the timer once more.
// This is going to be a very annoying app! ;-)
timerA.playFromStart()
}
// When the timer goes off, show the alert.
timerA.onFinished = {_ =>
alert1.show()
}
// Start the timer for the first time.
timerA.play
}

Gnome double click on touchscreen

I can't get double clicking to work on a Surface Pro 3 (Fedora; Kernel 4.28; Gnome 3.18, Gtk3). Two fast consecutive taps are not converted to a 2button event in Gtk+. See the small Python program below, which works fine for a normal mouse or touch pad.
I've checked that it is not a problem with the tap time and not the tap distance as you can see in the program. Where could this problem originate from? GDK_TOUCH_MASK?
To clarify, button events are received, but never converted to 2button event
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Double Click Test")
self.button = Gtk.Button.new_with_label("Double-click test\n\n")
self.button.connect("button-press-event", self.test_button_clicked)
self.button.connect("button-release-event", self.test_button_released)
settings = Gtk.Settings.get_default()
Gtk.Settings.set_property(settings,'gtk-double-click-distance',1000)
print Gtk.Settings.get_property(settings,'gtk-double-click-distance')
Gtk.Settings.set_property(settings,'gtk-double-click-time',1000)
print Gtk.Settings.get_property(settings,'gtk-double-click-time')
self.add(self.button)
def test_button_clicked(self, widget, event):
if event.type == Gdk.EventType._2BUTTON_PRESS:
widget.set_label(("Success!"))
GLib.timeout_add(1000, self.reset_test_button, widget)
return True
def test_button_released(self, widget, event):
return True
def reset_test_button(self, widget):
widget.set_label(("Double-click test"))
return False
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

Copy-Pasteable Text in pygtk's TreeView

I came across this rather unexpected problem. I have a gtk.TreeView with a single text column, that is rendered by gtk.CellRendererText. What I want is that the user can mark the displayed text using the mouse and get it into the clipboard by pressing ctrl+c. (I am referring to the very basic feature present in every webbrowser and texteditor). However, gtk won't let me do it. I have a simple example here, with non-markable / non-highlightable text:
import gtk
class TreeViewExample(gtk.TreeView):
def __init__(self):
gtk.TreeView.__init__(self)
self.get_selection().set_mode(gtk.SELECTION_NONE)
self.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL)
# create model
self.list_store = gtk.ListStore(str)
self.list_store.append(['Hello, this is some \n multiline text.'])
self.list_store.append(['Another text.'])
self.set_model(self.list_store)
# create text column
col = gtk.TreeViewColumn('Text Column')
self.append_column(col)
cell = gtk.CellRendererText()
col.pack_start(cell, True)
col.add_attribute(cell, 'text', 0)
class MasterWindow(object):
def destroy(self, widget, data=None):
gtk.main_quit()
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(500,500)
self.window.connect("destroy", self.destroy)
self.window.add(TreeViewExample())
self.window.show_all()
if __name__ == '__main__':
mw = MasterWindow()
gtk.main()
I could of course make the cell editable, because the editable mode provides the feature. But that is far from elegant, because it is some kind of popup, that breaks the line wrap and, well, edits the text. What i need is a cell, that is not selectable, editable or anything, but has text, that can be copied.
Does anyone have a solution? Thanks!
I made some changes in the code.
Try the code below, this example works well and I am sure that is the answer to your question.
Hope this helps you in the future of their knowledge and coding.
import gtk
class TreeViewExample():
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(500,500)
model = gtk.ListStore(str)
model.append(['a'])
model.append(['b'])
model.append(['c'])
treeview = gtk.TreeView(model)
self.renderer = gtk.CellRendererText()
self.renderer.set_property('editable', True)
treeview.insert_column_with_attributes(-1, 'Copy-Pastable-Editable String', self.renderer, text=0)
self.window.add(treeview)
self.window.show_all()
if __name__ == '__main__':
TreeViewExample()
gtk.main()
I made some changes in the code. Trying to answer questions of #Flimm.I hpe Helps you #Flimm.
import gtk
class TreeViewExample():
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(500,500)
self.model = gtk.ListStore(str,str)
self.model.append(['a','b'])
self.model.append(['c','a'])
self.model.append(['d','a'])
self.model.append(['e','a'])
self.treeview = gtk.TreeView(self.model)
self.renderer = gtk.CellRendererText()
self.renderer.set_property('editable', True)
self.renderer.connect('edited', self._text_changed, 0)
self.treeview.insert_column_with_attributes(-1, 'Copy-Pastable-Editable String', self.renderer, text=0)
self.treeview.insert_column_with_attributes(-1, 'Copy-Pastable-Editable String', self.renderer, text=1)
self.window.add(self.treeview)
self.window.show_all()
def _text_changed( self, w, row, new_value, column):
self.model[row][column] = new_value
if __name__ == '__main__':
TreeViewExample()
gtk.main()

Event Handling doesnot work for the following wxpython code.No error after I Run it.The Frame/buttons displayed

nO ERROR AT run TIME.NO OTHER DEF WORKS OTHER THAN INIT
import wx
import os
from spectral import *
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, size=(1000,70),style=wx.MINIMIZE_BOX|wx.CLOSE_BOX|wx.RESIZE_BORDER|wx.SYSTEM_MENU|wx.CAPTION|wx.CLIP_CHILDREN)
self.Bind(wx.EVT_CLOSE, self.OnClose)
panel=wx.Panel(self,-1)
self.button=wx.Button(panel,label="Open",pos=(0,0),size=(50,30))
self.button1=wx.Button(panel,label="Save",pos=(51,0),size=(50,30))
self.button2=wx.Button(panel,label="ROI",pos=(102,0),size=(50,30))
self.button3=wx.Button(panel,label="Tone",pos=(153,0),size=(50,30))
self.slider=wx.Slider(panel,pos=(204,0))
self.button4=wx.Button(panel,label="Header",pos=(305,0),size=(50,30))
self.SetBackgroundColour((11, 11, 11))
self.Bind(wx.EVT_ENTER_WINDOW, self.onMouseOver)
self.Bind(wx.EVT_LEAVE_WINDOW, self.onMouseLeave)
self.Bind(wx.EVT_BUTTON, self.OnButtonClick,self.button)
self.filename=""
def OnButtonClick(self,event):
dlg=wx.FileDialog(self,message="Choose a file", defaultDir="",defaultFile="", wildcard="*.*", style=wx.OPEN)
if dlg.ShowModal==wx.ID_OK:
dlg.Destroy()
def onMouseOver(self, event):
self.SetBackgroundColor((179, 179, 179))
self.Refresh()
def onMouseLeave(self, event):
self.SetBackgroundColor((11, 11, 11))
self.Refresh()
def OnClose(self, event):
dlg = wx.MessageDialog(self,
"Do you really want to close BBvw ?",
"Confirm Exit", wx.OK|wx.CANCEL|wx.ICON_QUESTION)
result = dlg.ShowModal()
dlg.Destroy()
if result == wx.ID_OK:
self.Destroy()
app = wx.App(redirect=True)
top = Frame("BBvw")
top.Show()
app.MainLoop()
nO ERROR AT run TIME.NO OTHER DEF WORKS OTHER THAN INIT.I think I am missing something obvious.'int' not callable error when buttons are clicked.
The OnButtonClick() method is getting called, but you're not calling ShowModal correctly. You need to put the parentheses on it: ShowModal()
Then that will work. The OnClose() works for me. The Mouse ones don't, but I'm not sure why. You should ask about those on the wxPython mailing list.