NSMenuItem + Python + Dynamic Variables - python

I'm trying to get a dynamic label for a menu item in this. I've written the entire app in Python but honestly with how much NSMenuItem looks, I might as well rewrite it in Objc...
import objc
from Foundation import *
from AppKit import *
from PyObjCTools import AppHelper
class MyApp(NSApplication):
def finishLaunching(self):
# Make statusbar item
statusbar = NSStatusBar.systemStatusBar()
self.statusitem = statusbar.statusItemWithLength_(NSVariableStatusItemLength)
self.icon = NSImage.alloc().initByReferencingFile_('icon.png')
self.icon.setScalesWhenResized_(True)
self.icon.setSize_((20, 20))
self.statusitem.setImage_(self.icon)
#make the menu
self.menubarMenu = NSMenu.alloc().init()
self.menuItem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Click Me', 'clicked:', '')
self.menubarMenu.addItem_(self.menuItem)
self.quit = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Quit', 'terminate:', '')
self.menubarMenu.addItem_(self.quit)
#add menu to statusitem
self.statusitem.setMenu_(self.menubarMenu)
self.statusitem.setToolTip_('My App')
def clicked_(self, notification):
NSLog('clicked!')
if __name__ == "__main__":
app = MyApp.sharedApplication()
AppHelper.runEventLoop()

Did you try setTitle_?
def clicked_(self, notification):
self.menuItem.setTitle_("Clicked!")
or with a timer:
def finishLaunching(self):
# ...
self.timer = NSTimer.alloc().initWithFireDate_interval_target_selector_userInfo_repeats_(NSDate.date(), 1.0, self, 'tick:', None, True)
NSRunLoop.currentRunLoop().addTimer_forMode_(self.timer, NSDefaultRunLoopMode)
self.timer.fire()
def tick_(self, arg):
self.menuItem.setTitle_("Tick %d!" % int(time.time()))
For live updates you probably need JGMenuWindow (SO: How to update NSMenu while it's open?)

Related

How to re-execute a file

I am trying to re-execute a file's code within the program
main.py
# Within the main.py
exec(open('PDM/Path2.py').read())
PDM/Path2.py
# Within the file being read
import tkinter as tkin
class path2_wind:
def __init__(self):
self.path2 = tkin.Tk()
self.path2.geometry('400x430')
self.path2.title('PDM/Path_2')
self.sight = tkin.PhotoImage(file='PDM/Path_2.Sight.png')
self.back = tkin.Label(self.path2,image=self.sight)
self.back.pack(side='top')
self.frame = tkin.Frame(self.path2)
self.hello = tkin.Label(self.frame,text='Welcome User (Name Here)')
self.back = tkin.Button(self.frame,text='Back',command = self.path2.destroy)
self.frame.pack(side='top')
self.hello.pack(side='left')
self.back.pack(side='left')
tkin.mainloop()
open = path2_wind()
The error displayed is TypeError: 'path2_wind' object is not callable.
It is because you have override the standard function open() by the line:
open = path2_wind()
So when the following code is being executed again:
exec(open('PDM/Path2.py').read())
it raises the exception since open is not the standard function now.
Use other name instead of open, for example:
win = path2_wind()
As #TheLizzard stated in the comment, it is better to use import instead of exec():
main.py
from PDM.Path2 import path2_wind
win = path2_wind()
...
PDM/Path2.py
import tkinter as tkin
class path2_wind:
def __init__(self):
self.path2 = tkin.Tk()
self.path2.geometry('400x430')
self.path2.title('PDM/Path_2')
#self.sight = tkin.PhotoImage(file='PDM/Path_2.Sight.png')
self.sight = tkin.PhotoImage(file='images/nier-a2.png')
self.back = tkin.Label(self.path2,image=self.sight)
self.back.pack(side='top')
self.frame = tkin.Frame(self.path2)
self.hello = tkin.Label(self.frame,text='Welcome User (Name Here)')
self.back = tkin.Button(self.frame,text='Back',command = self.path2.destroy)
self.frame.pack(side='top')
self.hello.pack(side='left')
self.back.pack(side='left')
tkin.mainloop()
if __name__ == '__main__':
win = path2_wind()

Text-Based adventure in Kivy, Python

I'm very new to programming, just have an introductory seminar in university, and I'm supposed to program a little app in Python. For that, I want to use Kivy, but I got stuck.
I have a text file which should include the question, the possible answers and where it's supposed to go considering which answer the user chose:
0|"Will you rather do A or B?"|"A"|"B"|1|2
1|"Congrats, you chose A. Now go to B."|"Go to B"|"Go to B"|2|2
2|"That's B. Incredible. Want to discover C?"|"Yes."|"Stay here."|3|6
3|Wow, C is so cool, isn't it? There's also a D.|D? Crazy!|Boring. Go back.|4|0
4|Welcome to the depths of D. You are curious, aren't you?|Yep.|Nope.|5|0
5|Cool. There's nothing else here.|There must be.|Knew it.|4|0
6|Surprise! You should really discover C.|Alright.|Is there a D?|3|4
Now I want the game to go to the according line, replace the displayed text and go on. In theory, this is kind of working with my Code (I'm sorry if it's messed up, as I said, I'm new to this topic):
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
with open('try.txt') as t:
Lines = t.readlines()
first_line = Lines[0].strip()
x = first_line.split("|")
answer_b = int(x[5])
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.cols = 1
self.inside = GridLayout()
self.inside.cols = 2
self.btna = Button(text=x[2])
self.btna.bind(on_press=self.a)
self.inside.add_widget(self.btna)
self.btnb = Button(text=x[3])
self.btnb.bind(on_press=self.b)
self.inside.add_widget(self.btnb)
self.main_text = Label(text=x[1])
self.add_widget(self.main_text)
self.add_widget(self.inside)
def a(self, instance):
answer_a = int(x[4])
next_line_a = Lines[answer_a].strip()
print(next_line_a)
print(answer_a)
x = next_line_a.split("|")
self.main_text.text = x[1]
self.btna.text = x[2]
self.btnb.text = x[3]
self.btna.bind(on_press=self.a)
def b(self, instance):
next_line_b = Lines[answer_b].strip()
print(next_line_b)
print(answer_b)
x = next_line_b.split("|")
self.main_text.text = x[1]
self.btna.text = x[2]
self.btnb.text = x[3]
class Game(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
Game().run()
The problem is that it stays with the first line I defined and I don't really know how to go around that problem. I would imagine that I first define x with the first line, and after that x gets redefined with the according new line. But the next_line and x variable are both dependent on each other - I tried two different ways with answer a and b, but both don't really work. B will just continuously take the first_line-x, A tells me that x is referenced before assignment.
It would be great if someone could help me out of my confusion, because everything I tried just didn't work out...
Thanks!
I changed it so you pass items into the object that you create. It's challenging to get the inheritance correct.
I also added an initializer to the Games object. I think this works but to be honest I am not expert in the workings of Kivy and have gotten this pattern to work but I don't know for sure if it is best practice.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
with open('try.txt') as t:
Lines = t.readlines()
class MyGrid(GridLayout):
def __init__(self, Lines: list):
super(MyGrid, self).__init__()
self.first_line = Lines[0].strip()
self.xx = self.first_line.split("|")
self.answer_b = int(self.xx[5])
self.cols = 1
self.inside = GridLayout()
self.inside.cols = 2
self.btna = Button(text=self.xx[2])
self.btna.bind(on_press=self.a)
self.inside.add_widget(self.btna)
self.btnb = Button(text=self.xx[3])
self.btnb.bind(on_press=self.b)
self.inside.add_widget(self.btnb)
self.main_text = Label(text=self.xx[1])
self.add_widget(self.main_text)
self.add_widget(self.inside)
def a(self, instance):
answer_a = int(self.xx[4])
next_line_a = Lines[answer_a].strip()
print(next_line_a)
print(answer_a)
self.xx = next_line_a.split("|")
self.main_text.text = self.xx[1]
self.btna.text = self.xx[2]
self.btnb.text = self.xx[3]
self.btna.bind(on_press=self.a)
def b(self, instance):
next_line_b = Lines[self.answer_b].strip()
print(next_line_b)
print(self.answer_b)
self.xx = next_line_b.split("|")
self.main_text.text = self.xx[1]
self.btna.text = self.xx[2]
self.btnb.text = self.xx[3]
class Game(App):
def __init__(self, **kwargs):
self._arguments_to_pass_through = kwargs
super().__init__()
def build(self):
return MyGrid(**self._arguments_to_pass_through)
if __name__ == '__main__':
Game(Lines=Lines).run()

Data from child window

In the imported code, the variable 'values' is set correctly with the date selected by the user.
The def selection is called at the exit of the calendar.
I'm stuck. I don't know how to catch it and use it in my main code.
Thanks a lot.
# MAIN CODE (simplified)
from tkinter import *
import calendarWidget
def manageCalendarWindow():
root4 = Tk()
data = {}
app = calendarWidget.Calendar(root4, data)
root4.mainloop()
manageCalendarWindow()
#how to get the date?
-.-.-.-.-.-.-.
# CALENDAR WIDGET (simplified)
class Calendar:
def setup(self, y, m)
(...)
for w, week in enumerate(self.cal.monthdayscalendar(y, m), 2):
for d, day in enumerate(week):
if day:
b = tk.Button(self.parent, width=1, text=day, relief = 'flat',\
command=lambda day=day:self.selection(day, calendar.day_name[(day-1) % 7]))
self.wid.append(b)
b.grid(row=w, column=d)
def selection(self, day, name):
self.day_selected = day
self.month_selected = self.month
self.year_selected = self.year
self.day_name = name
#data
self.values['day_selected'] = day
self.values['month_selected'] = self.month
self.values['year_selected'] = self.year
self.values['day_name'] = name
self.values['month_name'] = calendar.month_name[self.month_selected]
self.setup(self.year, self.month)
print(self.values) # <--- here the value is correct
self.parent.destroy()
-.-.-.-.-.-.-.-
THIS WORKS:
def manageCalendarWindow():
root4 = Tk()
data = {}
app = calendarWidget.Calendar(root4, data)
root4.mainloop()
return app
app=manageCalendarWindow()
print(app.year_selected,app.month_selected,app.day_selected)
THIS NOT:
class enterWindows():
def B_CalendarWindow(self):`
app=self.manageCalendarWindow()
print("year: ",app.year_selected)
print("and... this will never be printed!")
def manageCalendarWindow(self):
root4 = Tk()
data = {}
app = calendarWidget.Calendar(root4, data)
root4.mainloop()
return app
Everything local to the function manageCalendarWindow() is garbage collected when the function exits. This includes app (the class instance). You would have to return it to keep it alive. Note also that there is no self.month in the code you posted but I assume that comes from cutting the amount of code back for this post.
def manageCalendarWindow():
root4 = Tk()
data = {}
app = calendarWidget.Calendar(root4, data)
root4.mainloop()
return app
## or
## return app.day_name
app=manageCalendarWindow()
print(app.day_name)
## or
##day_name=manageCalendarWindow()
##print(day_name)
A simple proof-of concept program that gets a variable from a class instantiated within the class.
class SubClass():
def __init__(self):
self.variable="SubClass variable"
class MainClass():
def __init__(self):
app=self.instantiate_class()
print(app.variable) ## prints the string
def instantiate_class(self):
app=SubClass()
print("app =", app) ## not empty
return app
MainClass()
As mentioned in my comments, there can be only one mainloop in the code. The others will be simply ignored. So it's not possible to use it to wait for a response from a child window.
The solution I used is
app = myCalendar.Calendar(personal_path, root4, gui_language)
root4.wait_window(app.parent)
return app
The code opens the window and waits the result using wait_window().
Thanks all.
Ciao.

QCheckBox state change PyQt4

I'm trying to implement a system in PyQt4 where unchecking a checkbox would call function disable_mod and checking it would call enable_mod. But even though state is changing the checkboxes call the initial function they started with. For this case if an already checked box was clicked it'd always keep calling the disable_mod function! I don't understand why is this happening? Can you guys help me out here a little bit? Here's my code:
from PyQt4 import QtCore, QtGui
from os import walk
from os.path import join
import sys
def list_files_regex(dir):
l = []
for (root, dirnames, filenames) in walk(dir):
for d in dirnames:
list_files_regex(join(root, d))
l.extend(filenames)
return l
directory = "J:/test_pack"
directory = join(directory, "/mods")
count = 0
for y in list_files_regex(directory):
print y
count += 1
print count
class ModEdit(QtGui.QMainWindow):
def __init__(self, title, icon, x, y, w, h):
super(ModEdit, self).__init__()
self.setWindowTitle(title)
self.setWindowIcon(QtGui.QIcon(icon))
self.setGeometry(x, y, w, h)
self.choices = []
self.init()
def init(self):
scroll_widget = QtGui.QScrollArea()
sub_widget = QtGui.QWidget()
v_layout = QtGui.QVBoxLayout()
for y in list_files_regex(directory):
tmp = QtGui.QCheckBox(y, self)
tmp.resize(tmp.sizeHint())
if tmp.text()[len(tmp.text()) - 8: len(tmp.text())] != 'disabled':
tmp.setChecked(True)
# if tmp.isChecked() == 0:
# tmp.stateChanged.connect(self.enable_mod)
# if tmp.isChecked():
# tmp.stateChanged.connect(self.disable_mod)
# v_layout.addWidget(tmp)
self.choices.append(tmp)
print self.choices
for choice in self.choices:
v_layout.addWidget(choice)
if choice.isChecked():
choice.stateChanged.connect(self.disable_mod)
else:
choice.stateChanged.connect(self.enable_mod)
sub_widget.setLayout(v_layout)
scroll_widget.setWidget(sub_widget)
self.setCentralWidget(scroll_widget)
self.show()
def enable_mod(self):
print "ENABLE_TEST"
print self.choices[1].isChecked()
def disable_mod(self):
print "DISABLE_TEST"
print self.choices[1].isChecked()
def test(self):
print 'test'
for ch in self.choices:
if ch.isChecked():
ch.stateChanged.connect(self.disable_mod)
else:
ch.stateChanged.connect(self.enable_mod)
class Rename(QtCore.QObject):
enable = QtCore.pyqtSignal
disable = QtCore.pyqtSignal
app = QtGui.QApplication(sys.argv)
ex = ModEdit("Minecraft ModEdit", "ModEdit.png", 64, 64, 640, 480)
sys.exit(app.exec_())
The problem is that you're only connecting the checkbox stateChanged signal once during initialization. After the state of the checkbox changes, you're not disconnecting the signal and reconnecting it to the correct slot.
You'll need to connect the stateChanged signal to an intermediary slot that will decide which function to call based on the checkbox state. Since you're using the same slot for multiple checkboxes, it's probably best to also pass the checkbox to the slot as well.
from functools import partial
def init(self):
...
for tmp in list_of_checkboxes:
enable_slot = partial(self.enable_mod, tmp)
disable_slot = partial(self.disable_mod, tmp)
tmp.stateChanged.connect(lambda x: enable_slot() if x else disable_slot())
def enable_mod(self, checkbox):
print "ENABLE_TEST"
print checkbox.isChecked()
def disable_mod(self, checkbox):
print "DISABLE_TEST"
print checkbox.isChecked()
Alternatively, since we are now passing the checkbox to the slots, you could just use a single slot and check the checkbox state inside the slot
def init(self):
...
for tmp in list_of_checkboxes:
slot = partial(self.enable_disable_mod, tmp)
tmp.stateChanged.connect(lambda x: slot())
def enable_disable_mod(self, checkbox):
if checkbox.isChecked():
...
else:
...

QtSingleApplication for PySide or PyQt

Is there a Python version of the C++ class QtSingleApplication from Qt Solutions?
QtSingleApplication is used to make sure that there can never be more than one instance of an application running at the same time.
Here is my own implementation.
It has been tested with Python 2.7 and PySide 1.1.
It has essentially the same interface as the C++ version of QtSingleApplication. The main difference is that you must supply an application unique id to the constructor. (The C++ version by default uses the path to the executable as a unique id; that would not work here because the executable will most likely be python.exe.)
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtNetwork import *
class QtSingleApplication(QApplication):
messageReceived = Signal(unicode)
def __init__(self, id, *argv):
super(QtSingleApplication, self).__init__(*argv)
self._id = id
self._activationWindow = None
self._activateOnMessage = False
# Is there another instance running?
self._outSocket = QLocalSocket()
self._outSocket.connectToServer(self._id)
self._isRunning = self._outSocket.waitForConnected()
if self._isRunning:
# Yes, there is.
self._outStream = QTextStream(self._outSocket)
self._outStream.setCodec('UTF-8')
else:
# No, there isn't.
self._outSocket = None
self._outStream = None
self._inSocket = None
self._inStream = None
self._server = QLocalServer()
self._server.listen(self._id)
self._server.newConnection.connect(self._onNewConnection)
def isRunning(self):
return self._isRunning
def id(self):
return self._id
def activationWindow(self):
return self._activationWindow
def setActivationWindow(self, activationWindow, activateOnMessage = True):
self._activationWindow = activationWindow
self._activateOnMessage = activateOnMessage
def activateWindow(self):
if not self._activationWindow:
return
self._activationWindow.setWindowState(
self._activationWindow.windowState() & ~Qt.WindowMinimized)
self._activationWindow.raise_()
self._activationWindow.activateWindow()
def sendMessage(self, msg):
if not self._outStream:
return False
self._outStream << msg << '\n'
self._outStream.flush()
return self._outSocket.waitForBytesWritten()
def _onNewConnection(self):
if self._inSocket:
self._inSocket.readyRead.disconnect(self._onReadyRead)
self._inSocket = self._server.nextPendingConnection()
if not self._inSocket:
return
self._inStream = QTextStream(self._inSocket)
self._inStream.setCodec('UTF-8')
self._inSocket.readyRead.connect(self._onReadyRead)
if self._activateOnMessage:
self.activateWindow()
def _onReadyRead(self):
while True:
msg = self._inStream.readLine()
if not msg: break
self.messageReceived.emit(msg)
Here is a simple test program:
import sys
from PySide.QtGui import *
from QtSingleApplication import QtSingleApplication
appGuid = 'F3FF80BA-BA05-4277-8063-82A6DB9245A2'
app = QtSingleApplication(appGuid, sys.argv)
if app.isRunning(): sys.exit(0)
w = QWidget()
w.show()
app.setActivationWindow(w)
sys.exit(app.exec_())
You can have a look to this blog entry. It is for Pyside but I guess that it will work too with PyQt4.

Categories