first time posting!
I'm fairly new to python (and programing!), and I have started to make a basic tkinter program. The code is getting pretty long now, and I want to split it between different .py files to make it more navigatable. So far, all my code exists in classes, seperating the main window, from calculation functions, secondary windows and so on.
First question, is it considered good practice to split code like this? I feel like it is, but want to make sure!
Secondly, how is the best way to handle modules between files?
For example, I have tkinter and matplotlib imported in the main_window.py file. I have the main_window class function which calls a different class which I want to move to another file, but this secondary class has a line which calls tkinter. I want to pass self through the secondary function so that it is using the same instance, as it were.
Here is an example code to illustrate. The first file, main_window.py:
# main_window.py
import tkinter as tk
import matplotlib
import matplotlib.pyplot as plt
import app_design # app_design.py contains the code I want to break out
class MainWindow:
def __intit__(self, root):
self.root = root
self.start_gui()
def start_gui(self):
# a bunch of code
...
# in reality this is read from a program file on startup
color_mode = "Dark"
# This is just an example, the matplotlib call in the other app is in the __init__
self.bg_col_mode = tk.StringVar()
self.bg_col_mode.set(app_design.AppColors(color_mode).background())
# a bucnh more code of various tk widgets and matplotlib charts
...
if __name__ == '__main__':
app = MainWindow(tk.Tk())
app.root.mainloop()
and then an example of some code which I'd like to split out. This is not the only instance of the class referencing a module outside the MainWindow class, but it works as an example:
# app_design.py
class AppColors:
def __init__(self, color_mode):
self.color_mode = color_mode
if self.col_mode == "Dark":
self.plt.style.use("Dark") # it is this .plt call that has moved from the main_window.py file to the new file
else:
self.plt.style.use("Light")
def background_mode(self):
if self.color_mode == "Dark":
return "#292929" # colour code
else:
return "#F8F1F1"
Hopefully this makes sense!
First question, is it considered good practice to split code like this? I feel like it is, but want to make sure!
I actually don't know myself, I only have coded stuff for backend.
Secondly, how is the best way to handle modules between files?
You simply import the file (or function directly).
Example:
file1.py
def hello(name):
print("Hello ", name)
file2.py
from file1 import hello
hello("arjix")
this way you can directly use the function hello
or
import .file1
file1.hello("arjix")
PS: Make sure these two files are in the same folder.
Related
Problem
I am building in app (with Kivy) which renders some custom widgets to the screen. Unfortunately, their positions (relative to other widgets) changes when the tablet on which I am running the app is rotated. Now I have built a function that repositions them, and I would like to call this function when the tablet is rotated. How can I implement this?
My attempts so far...
I have seen from the Kivy documentation that the Window class has an on_rotate event, and have tried to incorporate this into my program in several different ways.
First, I tried implementing the callback after the if __name__ == '__main__' statement. Something like:
if __name__ == '__main__':
app = MainApp()
Window.on_rotate = lambda: app.reposition_widgets()
app.run()
That did nothing. In vain I also tried:
if __name__ == '__main__':
app = MainApp()
Window.on_rotate(app.reposition_widgets)
app.run()
I then attempted to bind the widgets to on_rotate events, something like:
class CustomWidget(Widget):
def __init__(self, **kwargs)
super().__init__(**kwargs)
self.bind(on_rotate = self.reposition_widgets)
That did nothing. Neither did:
class CustomWidget(Widget):
def __init__(self, **kwargs)
super().__init__(**kwargs)
on_rotate = reposition_widgets
My final attempt was to create a Window class in the accompanying kv file and the Python file, then specify the on_rotate event from there. Something like:
#kv file
Window:
on_rotate: app.reposition_widgets()
That didn't work either.
Possible work around
To be honest, rotating the screen is not all that useful for my app, and so it wouldn't be all that bad if I just disabled screen rotation. However, I was not able to find a good way of doing so in the documentation. Do you know how I would go about doing this?
Binding the widgets to the on_pos event - or something similar. Something like:
class CustomWidget:
def reposition_widgets():
# Code here
on_pos = reposition_widgets
The problem with this is that the widgets move about a lot, which means that the function gets called a lot - causing problems elsewhere.
Let me know if you would like to see more code. I have over 1000 lines spread over several files, and felt that just copy-and-pasting wouldn't be particularly useful.
I was able to solve this issue using the on_size event.
class CustomWidget(Widget):
def reposition_widgets(self):
# Reposition logic here
on_size = reposition_widgets
This worked quite well actually. I added an additional parameter in the reposition_widgets function to differentiate between when I was repositioning because of on_size and when I was repositioning because I was calling it (see example below). However, this is not always necessary.
class CustomWidget(Widget):
def reposition_widgets(self, rotation=False):
if rotation:
# When function is called due to on_size
else:
# When function is called in code
on_size = lambda self, *args: self.reposition_widgets(rotation=True)
I have created two python programs. One in which I am doing all the manipulations like sorting the data in Excel file using xlrd and xlwt. Another is I created a GUI using Tkinter and importing the original Excel file on which I need to do the manipulations
My question is how to add this program for manipulation in Tkinter program so that on click I will get required file with all the manipulations done. Both the programs are working individually
Use the import keyword:
Put both files in the same directory, name them like variables (the name does not start from a number, contain dashes etc. Example: gui.py and operations.py)
Put everything from the operations file (except imports) in a function. Example:
import random
def main():
for x in range(10):
print(random.randint(1,10))
Use the import keyword:
from tkinter import Tk, Button
import operations
tk = Tk()
Button(tk, command=operations.main).pack()
tk.mainloop()
where operations (twice) is the name of your file with a function, minus the .py part, and main - the name of a functon.
There's a different way, a bad one, that's os-specific but does not require the main function. Depending on the OS you could try:
import os
os.system('python3 operations.py')# variation 1
os.system('python operations.py')# variation 2
os.system('py -3 operations.py')# variation 3
Hope that's helpful!
I write a python script (named script2.py) which includes tkinter. To be convenient, I write a function at the bottom. It looks like:
import tkinter
import pygubu
class GuiApp2:
def __init__(self,master):
self.builder=pygubu.Builder()
self.builder.add_from_file('test.ui')
self.builder.get_object('Frame_main',master)
self.list_box_a=self.builder.get_object('Listbox_a')
self.lba_value_set=tkinter.StringVar()
self.list_box_a['listvariable']=self.lba_value_set
def set_value_set(self,the_value_set):
self.lba_value_set.set(the_value_set)
def run(the_value_set):
master=tkinter.Tk()
app=GuiApp1(master)
app.set_value_set(the_value_set)
master.mainloop()
def main():
run(['1','2','3'])
if __name__ == '__main__':
main()
And then I write another script (named script1.py) which calls the function at the bottom of the script above. It is:
import tkinter
import pygubu
import script2
class GuiApp1:
def __init__(self,master):
self.builder=pygubu.Builder()
self.builder.add_from_file('mainapp.ui')
self.builder.get_object('Frame_main',master)
self.button_show=self.builder.get_object('Button_show')
self.button_show['command']=self.command_for_button_show
def command_for_button_show(self):
script2.run(['1','2','3'])
def main():
master=tkinter.Tk()
app=GuiApp1(master)
master.mainloop()
if __name__ == '__main__':
main()
When I run script2.py, everything is fine. But when I run script1.py which imports script2.py the Listbox in script2.py is empty.
Of course, these two scripts are not the files I use in my project. The files I really use is too long and difficult to read.
In order to find out the problem, I inserted several print functions in my script to show the values of the variable in my scripts. Finally, every print result is fine except the Listbox.
Thus I simplified the real scripts to these scripts which are easy to read.
I guess maybe the master(tkinter.Tk()) in script1.py affect the master in script2.py. Because the logic of GUI Management in tkinter is different from it in dotNet.
Is there anyone who's met a similar problem or have some idea about that?
Yes, they are independent. Each time you create an instance of Tk, you create a new instance of an embedded Tcl interpreter. As a consequence, the widgets in one know nothing about the widgets in another.
A well written tkinter program should never create more than one instance of Tk. If you need additional windows you should create instances of Toplevel. You also should call mainloop() exactly once.
I am a newbie in Python and am creating a Tkinter GUI that basically allows users to find desired keywords in .docx files. I have two buttons that both load a pickle file and only one may also add content to the file and save. After using the file under one function(button) and trying to load it up again under the other function(button) the file does not load. It will only load up again after I fully exit the program and run it again. I am not using classes for this program, but I feel that I may have to. I am just not able to bounce between these functions and successfully pass the file. Any advice would be greatly appreciated. Here is the code that may be most useful for this question:
from Tkinter import *
import tkMessageBox
import sys,os,glob,pickle
root = Tk()
root.geometry('400x300')
root.title('Potential Candidate Engine')
Pickle_File = ('Diction.pickle')
if os.path.exists(Pickle_File):
with open(Pickle_File,'r') as rPF:
Dictionary = pickle.load(rPF)
def Clear_Window():
RETURN = sys.executable
os.execl(RETURN, RETURN, * sys.argv)
def SR():
global Dictionary
### Scan the document here ###
def CNP():
global Dictionary
### User adds content here, file saves ###
That 'Clear_Window()' function is just a workaround to return to the main window after the user is done with one function and would like to use the other. It is executed when the RETURN button is pushed
I'm hoping someone can help me with a Qt designer question. I'm trying to modify GUI elements from outside the class calling the GUI file. I've set up example code showing the structure of my programs. My goal is to get func2, in the main program (or another class) to change the main window's statusbar.
from PyQt4 import QtCore, QtGui
from main_gui import Ui_Main
from about_gui import Ui_About
#main_gui and about_gui are .py files generated by designer and pyuic
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_Main()
self.ui.setupUi(self)
self.ui.actionMyaction.triggered.connect(self.func1)
#Signals go here, and call this class's methods, which call other methods.
#I can't seem to call other methods/functions directly, and these won't take arguments.
def func1(self):
#Referenced by the above code. Can interact with other classes/functions.
self.ui.statusbar.showMessage("This works!")
def func2(self):
StartQT4.ui.statusbar.showMessage("This doesn't work!")
#I've tried many variations of the above line, with no luck.
#More classes and functions not directly-related to the GUI go here; ie the most of the program.
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
I'm trying to get func2 to work, since I don't want my whole program to be under the StartQT4 class. I've tried many variations of that line, but can't seem to access GUI items from outside of this class. I've tried sending signals as well, but still can't get the syntax right.
It's possible that my structure is bogus, which is why I posted most of it. Essentially I have a .py file created by Designer, and my main program file, which imports it. The main program file has a class to initiate the GUI, (and a class for each separate window). It has signals in this class, that call methods in the class. These methods call functions from my main program, or other classes I've created. The end of the program has the if __name__ == "__main__" code, to start the GUI. Is this structure bogus? I've read many tutorials online, all different, or outdated.
Your func1 method is a way to go - since ui is a field in StartQT4 class, you should directly manipulate with its data only within the same class. There is nothing wrong that you have all user interface functionality for one widget in one class - it is not a big issue if you have only two classes in your code, but having several classes to reference the fields directly is potential nightmare for maintentace (what if you change the name of statusbar widget?).
However, if you actually want to edit it from func2, then you need to pass the reference of StartQT4 object to it, because you need to specify for what instance of window you need to change status bar message.
def func2(qtWnd): # Self should go here if func2 is beloning to some class, if not, then it is not necessary
qtWnd.ui.statusbar.showMessage("This should work now!")
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
func2(myapp)
sys.exit(app.exec_())