Readme creator update to match latest releases. Component parts of Readme
This commit is contained in:
parent
7c723133e0
commit
385c2020f0
|
@ -28,19 +28,25 @@ HOW DO I INSERT IMAGES ???
|
|||
![pysimplegui_logo](https://user-images.githubusercontent.com/13696193/43165867-fe02e3b2-8f62-11e8-9fd0-cc7c86b11772.png)
|
||||
|
||||
[![Downloads](http://pepy.tech/badge/pysimplegui)](http://pepy.tech/project/pysimplegui) tkinter
|
||||
|
||||
[![Downloads ](https://pepy.tech/badge/pysimplegui27)](https://pepy.tech/project/pysimplegui27) tkinter 2.7
|
||||
|
||||
[![Downloads](https://pepy.tech/badge/pysimpleguiqt)](https://pepy.tech/project/pysimpleguiqt) Qt
|
||||
|
||||
[![Downloads](https://pepy.tech/badge/pysimpleguiwx)](https://pepy.tech/project/pysimpleguiWx) WxPython
|
||||
|
||||
[![Downloads](https://pepy.tech/badge/pysimpleguiweb)](https://pepy.tech/project/pysimpleguiWeb) Web (Remi)
|
||||
|
||||
![Documentation Status](https://readthedocs.org/projects/pysimplegui/badge/?version=latest)
|
||||
|
||||
![Awesome Meter](https://img.shields.io/badge/Awesome_meter-100-yellow.svg)
|
||||
|
||||
![Python Version](https://img.shields.io/badge/Python-2.7_3.x-yellow.svg)
|
||||
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-4.2.0-red.svg?longCache=true&style=for-the-badge)
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-4.4.0-red.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-2.2.0-blue.svg?longCache=true&style=for-the-badge)
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-2.4.0-blue.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUIQt_Version-0.26.0-orange.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
|
@ -119,11 +125,15 @@ HOW DO I INSERT IMAGES ???
|
|||
|
||||
|
||||
|
||||
#### Quick Links To Help and Read Up on the Latest News and Releases
|
||||
|
||||
[ReadTheDocs](http://www.PySimpleGUI.org) <------ THE best place to read the docs due to TOC, all docs in 1 place, and better formatting. START here in your education.
|
||||
### START HERE - User Manual with Table of Contents
|
||||
|
||||
[Homepage - Lastest Readme and Code - GitHub](http://www.PySimpleGUI.com)
|
||||
[ReadTheDocs](http://www.PySimpleGUI.org) <------ THE best place to read the docs due to TOC, all docs in 1 place, and better formatting. START here in your education. Easy to remember PySimpleGUI.org.
|
||||
|
||||
#### Quick Links To Help and The Latest News and Releases
|
||||
|
||||
|
||||
[Homepage - Lastest Readme and Code - GitHub](http://www.PySimpleGUI.com) Easy to remember: PySimpleGUI.com
|
||||
|
||||
[Announcements of Latest Developments, Release news, Misc](https://github.com/PySimpleGUI/PySimpleGUI/issues/142)
|
||||
|
||||
|
@ -211,15 +221,15 @@ PySimpleGUI runs on Windows, Linux and Mac, just like tkinter, Qt, WxPython and
|
|||
* Windows 7, 8, 10
|
||||
* Linux on PC - Tested on **many** distributions
|
||||
* Linux on Raspbnerry Pi
|
||||
* Linux on Android - Must use either Termux or PyDroid3
|
||||
* Mac OS (Sorry I don't know much about Macs)
|
||||
* Linux on Android - Can use either Termux or PyDroid3
|
||||
* Mac OS (Sorry don't know much about Macs other than Macs don't like tkinter)
|
||||
|
||||
|
||||
#### Python versions
|
||||
|
||||
As of 9/25/2018 **both Python 3 and Python 2.7 are supported** when using **tkinter version** of PySimpleGUI! The Python 3 version is named `PySimpleGUI`. The Python 2.7 version is `PySimpleGUI27`. They are installed separately and the imports are different. See instructions in Installation section for more info. **None** of the other ports can use Python 2.
|
||||
|
||||
Note that the 2.7 port will cease to exist on this GitHub on Jan 1, 2020. If you would like to know how much time you have to move over to the Python 3 version of PySimpleGUI, then go here: https://pythonclock.org/. The only thing that will be available is an unsupported PyPI release of PySimpleGUI27.
|
||||
Note that the 2.7 port will *cease to exist on this GitHub* on Jan 1, 2020. If you would like to know how much time you have to move over to the Python 3 version of PySimpleGUI, then go here: https://pythonclock.org/. The only thing that will be available is an unsupported PyPI release of PySimpleGUI27.
|
||||
|
||||
By "will cease to exist on this GitHub" I mean, it will be deleted entirely. No source code, no supporting programs. Nothing. If you're stuck using 2.7 in December, it would behoove you to fork the 2.7 code on Dec 31, 2019. Legacy Python doesn't have a permanent home here.
|
||||
|
||||
|
@ -949,6 +959,7 @@ Features of PySimpleGUI include:
|
|||
- Movable windows
|
||||
- Animated GIFs
|
||||
- No async programming required (no callbacks to worry about)
|
||||
- Built-in debugger and REPL
|
||||
- User expandable by accessing underlying GUI Framework widgets directly
|
||||
|
||||
---
|
||||
|
@ -986,7 +997,7 @@ The Single Line Progress Meter is a good example. It requires one and only 1 lin
|
|||
|
||||
Be Pythonic...
|
||||
|
||||
This one is difficult for me to define. The code implementing PySimpleGUI isn't PEP8 compliant, but it is consistent. The important thing was what the user saw and experienced while coding, NOT the choices for naming conventions in the implementation code.
|
||||
This one is difficult for me to define. The code implementing PySimpleGUI isn't PEP8 compliant, but it is consistent. The important thing was what the user saw and experienced while coding, NOT the choices for naming conventions in the implementation code. The user interface to PySimpleGUI now has a PEP8 compliant interface. The methods are snake_case now (in addition to retaining the older CamelCase names)
|
||||
|
||||
I ended up defining it as - attempt to use language constructs in a natural way and to exploit some of Python's interesting features. It's Python's lists and optional parameters make PySimpleGUI work smoothly.
|
||||
|
||||
|
@ -1001,9 +1012,11 @@ Here are some Python-friendly aspects to PySimpleGUI:
|
|||
|
||||
#### Lofty Goals
|
||||
|
||||
> Change Python
|
||||
> Teach GUI Programming to Beginners
|
||||
|
||||
The hope is **not** that ***this*** package will become part of the Python Standard Library. The hope is that Python will become ***the*** go-to language for creating GUI programs that run on Windows, Mac, and Linux *for all levels of developer*. Perhaps this sort of package is needed to make that happen. It would be nice if there was a "unified interface" for GUIs like PySimpleGUI presents, along with the vendor specific calls.
|
||||
By and large PySimpleGUI is a "pattern based" SDK. Complete beginners can copy these standard design patterns or demo programs and modify them without necessarily understanding all of the nuts and bolts of what's happening. For example, they can modify a layout by adding elements even though they may not yet grasp the list of lists concept of layouts.
|
||||
|
||||
Beginners certainly can add more `if event == 'my button':` statements to the event loop that they copied from the same design pattern. They will not have to write classes to use this package.
|
||||
|
||||
> Capture Budding Graphic Designers & Non-Programmers
|
||||
|
||||
|
@ -1131,12 +1144,13 @@ When you type sg, Python will tell you the full patch to your PySimpleGUI file /
|
|||
|
||||
### Finding Out Where Your PySimpleGUI Is Coming From (from within your code)
|
||||
|
||||
If you continue to have troubles with getting the right version of PySimpleGUI loaded, THE ***definitive*** way to determine where your program is getting PySimpleGUI from is to add a print to your program. It's that *simple*!
|
||||
If you continue to have troubles with getting the right version of PySimpleGUI loaded, THE ***definitive*** way to determine where your program is getting PySimpleGUI from is to add a print to your program. It's that *simple*! You can also get the version you are running by also printing
|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
print(sg)
|
||||
print(sg.version)
|
||||
```
|
||||
|
||||
Just like when using the REPL >>> to determine the location, this `print` in your code will display the same path information.
|
||||
|
@ -1149,6 +1163,7 @@ If you're not connected to the net on your target machine, or pip isn't working,
|
|||
Be ***sure*** that you delete this PySimpleGUI.py file if you install a newer pip version. Often the sequence of events is that a bug you've reported was fixed and checked into GitHub. You download the PySimpleGUI.py file (or the appropriately named one for your port) and put with your app. Then later your fix is posted with a new release on PyPI. You'll want to delete the GitHub one before you install from pip.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Python 2.7 or Python 3
|
||||
tkinter
|
||||
|
||||
|
@ -1214,12 +1229,11 @@ Then use either "high level" API calls or build your own windows.
|
|||
|
||||
Yes, it's just that easy to have a window appear on the screen using Python. With PySimpleGUI, making a custom window appear isn't much more difficult. The goal is to get you running on your GUI within ***minutes***, not hours nor days.
|
||||
|
||||
***WARNING*** Do NOT use PySimpleGUI with Python 3.7.4. tkiter is having issues with that release. Things like Table colors stopped working entirely.
|
||||
***WARNING*** Do NOT use PySimpleGUI with Python 3.7.3 and 3.7.4. tkiter is having issues with that release. Things like Table colors stopped working entirely. None of us want to debug tkinter code. It's difficult enough debugging your code and PySimpleGUI code. A lot of time has already been spent debugging this one so no need for you to suffer too.
|
||||
|
||||
### Python 3.7
|
||||
|
||||
It puzzles me why a beginner would install 3.7. Or even a seasoned programmer. What specific feature of 3.7 are you using that is not in 3.6? If you are unable to answer this, then ***you should be running 3.6***, an immensely solid release of Python. If you must run 3.7, try 3.7.2 instead. It does work with PySimpleGUI with no known issues.
|
||||
|
||||
It puzzles me why a beginner would install 3.7. Or even a seasoned programmer. What specific feature of 3.7 are you using that is not in 3.6? If you are unable to answer this, then ***it's strongly suggested that you run 3.6***, an immensely solid release of Python with all those goodie inside like f-strings. If you must run 3.7, try 3.7.2 instead. It does work with PySimpleGUI with no known issues.
|
||||
|
||||
|
||||
## Using - Python 2.7
|
||||
|
@ -1246,6 +1260,7 @@ else:
|
|||
|
||||
This will automatically import the correct library based on the Python version number reported by the Python interpreter.
|
||||
|
||||
NOTE: It's 2019 and 2.7 support is being systematically removed. This construct will be removed from the demo programs shortly. 2.7 users can still run these demos, but they will need to change the import from PySimpleGUI to PySimpleGUI27. It save 4 lines of code and an import from sys in the process.
|
||||
|
||||
---
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,8 +10,9 @@ There are too many to list!!
|
|||
|
||||
There are over 170 sample programs to give you a jump start.
|
||||
|
||||
These programs are an integral part of the overall PySimpleGUI documentation and learning system. They will give you a headstart in a way you can learn from and understand. They also show you integration techiques to other packages that have been figured out for you.
|
||||
|
||||
You will find Demo Programs located in a subfolder named "Demo Programs" under each of the PySimpleGUI ports on GitHub.
|
||||
You will find Demo Programs located in a subfolder named "Demo Programs" under the top level and each of the PySimpleGUI ports on GitHub.
|
||||
|
||||
Demo programs for plain PySimpleGUI (tkinter)
|
||||
https://github.com/PySimpleGUI/PySimpleGUI/tree/master/DemoPrograms
|
||||
|
@ -37,6 +38,12 @@ There are not many programs under each of the port's folders because the main De
|
|||
* [Mido](https://github.com/olemb/mido)
|
||||
* [Matplotlib](https://matplotlib.org/)
|
||||
* [PyMuPDF](https://github.com/rk700/PyMuPDF)
|
||||
* OpenCV
|
||||
* pymunk
|
||||
* psutil
|
||||
* pygame
|
||||
* Forecastio
|
||||
|
||||
|
||||
|
||||
# Creating a Windows .EXE File
|
||||
|
@ -99,34 +106,56 @@ For a fun time, add these lines to the top of your script
|
|||
```
|
||||
This will turn all of your print statements into prints that display in a window on your screen rather than to the terminal.
|
||||
|
||||
# Look and Feel (`ChangleLookAndFeel`)
|
||||
# Look and Feel
|
||||
|
||||
You can change defaults and colors of a large number of things in PySimpleGUI quite easily.
|
||||
|
||||
## `ChangleLookAndFeel`
|
||||
|
||||
Want a quick way of making your windows look a LOT better? Try calling `ChangeLookAndFeel`. It will, in a single call, set various color values to widgets, background, text, etc.
|
||||
|
||||
Or dial in the look and feel (and a whole lot more) that you like with the `SetOptions` function. You can change all of the defaults in one function call. One line of code to customize the entire GUI.
|
||||
|
||||
While you can define colors for each individual element and you can even define some on a windows wide basis, but it requires setting a lot of different settings.
|
||||
Dial in the look and feel that you like with the `SetOptions` function. You can change all of the defaults in one function call. One line of code to customize the entire GUI.
|
||||
Or beginning in version 2.9 you can choose from a look and feel using pre-defined color schemes. Call ChangeLookAndFeel with a description string.
|
||||
|
||||
```python
|
||||
sg.ChangeLookAndFeel('GreenTan')
|
||||
|
||||
```
|
||||
|
||||
Valid values for the description string are:
|
||||
Valid look and feel values are currently:
|
||||
|
||||
```python
|
||||
SystemDefault
|
||||
Reddit
|
||||
Topanga
|
||||
GreenTan
|
||||
Dark
|
||||
LightGreen
|
||||
Dark2
|
||||
Black
|
||||
Tan
|
||||
TanBlue
|
||||
DarkTanBlue
|
||||
DarkAmber
|
||||
DarkBlue
|
||||
Reds
|
||||
Green
|
||||
BluePurple
|
||||
Purple
|
||||
BlueMono
|
||||
GreenMono
|
||||
BrownBlue
|
||||
BrightColors
|
||||
NeutralBlue
|
||||
Kayak
|
||||
SandyBeach
|
||||
TealMono
|
||||
```
|
||||
|
||||
GreenTan
|
||||
LightGreen
|
||||
BluePurple
|
||||
Purple
|
||||
BlueMono
|
||||
GreenMono
|
||||
BrownBlue
|
||||
BrightColors
|
||||
NeutralBlue
|
||||
Kayak
|
||||
SandyBeach
|
||||
TealMono
|
||||
|
||||
The way this call actually works is that it calls `SetOptions` with a LOT of color settings. Here is the actual call that's made. As you can see lots of stuff is defined for you.
|
||||
|
||||
|
||||
|
||||
```python
|
||||
SetOptions(background_color=colors['BACKGROUND'],
|
||||
text_element_background_color=colors['BACKGROUND'],
|
||||
|
@ -151,10 +180,12 @@ SetOptions(background_color=colors['BACKGROUND'],
|
|||
<!-- <+func.ChangeLookAndFeel+> -->
|
||||
|
||||
|
||||
To see the latest list of color choices, take a look at the bottom of the `PySimpleGUI.py` file where you'll find the `ChangLookAndFeel` function.
|
||||
To see the latest list of color choices you can call `ListOfLookAndFeelValues()`
|
||||
|
||||
You can also combine the `ChangeLookAndFeel` function with the `SetOptions` function to quickly modify one of the canned color schemes. Maybe you like the colors but was more depth to your bezels. You can dial in exactly what you want.
|
||||
|
||||
|
||||
|
||||
**ObjToString**
|
||||
Ever wanted to easily display an objects contents easily? Use ObjToString to get a nicely formatted recursive walk of your objects.
|
||||
This statement:
|
||||
|
@ -179,16 +210,39 @@ You'll quickly wonder how you ever coded without it.
|
|||
|
||||
---
|
||||
# Known Issues
|
||||
|
||||
Well, there are a few quirks, and problems of course. Check the [GitHub Issues database](https://github.com/PySimpleGUI/PySimpleGUI/issues) for a list of them.
|
||||
|
||||
As previously mentioned this is also where you should post all problems and enhancements.
|
||||
|
||||
|
||||
## MACS + tkinter = SUCKS
|
||||
|
||||
Not sure why, but for over a year and a half, setting the color of buttons does not work on Macs. There have been numerous other problems. Checking the Issues database is the best place to see what they are. If there was a magic wand it would have been used long ago to fix these problems, but there does not appear to be a magic fix.
|
||||
|
||||
This was already mentioned at the top of this document but want to make sure it's covered as a "known issue"
|
||||
|
||||
## Multiple threads
|
||||
|
||||
While not an "issue" this is a ***stern warning***
|
||||
|
||||
## **Do not attempt** to call `PySimpleGUI` from multiple threads! It's `tkinter` based and `tkinter` has issues with multiple threads
|
||||
|
||||
**Progress Meters** - the visual graphic portion of the meter may be off. May return to the native tkinter progress meter solution in the future. Right now a "custom" progress meter is used. On the bright side, the statistics shown are extremely accurate and can tell you something about the performance of your code. If you are running 2 or more progress meters at the same time using `OneLineProgressMeter`, you need to close the meter by using the "Cancel" button rather than the X
|
||||
Tkinter also wants to be the MAIN thread in your code. So, if you have to run multiple threads, make sure the GUI is the main thread.
|
||||
|
||||
**Async windows** - these include the 'easy' windows (`OneLineProgressMeter` and EasyPrint/Print). If you start overlapping having Async windows open with normal windows then things get a littler squirrelly. Still tracking down the issues and am making it more solid every day possible. You'll know there's an issue when you see blank window.
|
||||
Other than that, feel free to use threads with PySimpleGUI on all of the ports. You'll find a good example for how to run "long running tasks" in your event loop by looking at the demo program: `Demo_Multithreaded_Long_Tasks.py`
|
||||
|
||||
**EasyPrint** - EasyPrint is a new feature that's pretty awesome. You print and the output goes to a window, with a scroll bar, that you can copy and paste from. Being a new feature, it's got some potential problems. There are known interaction problems with other GUI windows. For example, closing a Print window can also close other windows you have open. For now, don't close your debug print window until other windows are closed too.
|
||||
|
||||
## Contributing
|
||||
# Contributing
|
||||
|
||||
Core code pull requests are not being accepted at this time.
|
||||
## Core Code
|
||||
|
||||
***Core code changes/pull requests are not being accepted at this time.***
|
||||
|
||||
## Demos
|
||||
|
||||
You're welcome to share a PySimpleGUI program you've written that you think fits the model of a PySimpleGUI Demo Program.
|
||||
|
||||
## GitHub Repos
|
||||
|
||||
If you've created a GitHub for your project that uses PySimpleGUI then please submit it to be included in this document or on the PySimpleGUI GitHub site. Also, you'll find a lot more people will look at your code, explore your repo if you have posted **screen shots in your readme**. People *love* success stories and showing your GUI's screen shows you've been successful. Everyone wins!
|
||||
|
|
|
@ -707,7 +707,7 @@ Long time coming. Docstrings continue to be a focus.
|
|||
* Fix for debugger trying to execute a REPL comand. The exec is only avilable in Python 3
|
||||
* main() will display the version number in big letters when program is running
|
||||
|
||||
### 4.2 PySimpleGUI 2.2 for PySimpleGUI27 8 - Aug 2019
|
||||
### 4.2 PySimpleGUI 2.2 for PySimpleGUI27 18 - Aug 2019
|
||||
|
||||
The cool lookup release! No more need for FindElement. You can continue to use FindElement.
|
||||
However, your code will look weird and ancient. ;-) (i.e. readable)
|
||||
|
@ -735,6 +735,74 @@ MORE Docstring and main doc updates!
|
|||
* Fixed sizing Columns! NOW they will finally be the size specified
|
||||
* Fixed not using the initialdir paramter in PopupGetFile if the no_window option is set
|
||||
|
||||
## 4.3 PySimpleGUI Release 22-Aug-2019
|
||||
|
||||
PEP8 PEP8 PEP8
|
||||
Layout controls! Can finally center stuff
|
||||
Some rather impactful changes this time
|
||||
Let's hope it doesn't all blow up in our faces!
|
||||
|
||||
* PEP8 interfaces added for Class methods & functions
|
||||
* Finally a PEP8 compliant interface for PySimpleGUI!!
|
||||
* The "old CamelCase" are still in place and will be for quite some time
|
||||
* Can mix and match at will if you want, but suggest picking one and sticking with it
|
||||
* All docs and demo programs will need to be changed
|
||||
* Internally saving parent row frame for layout checks
|
||||
* Warnings on all Update calls - checks if Window.Read or Window.Finalize has been called
|
||||
* Warning if a layout is attempted to be used twice
|
||||
* Shows an "Error Popup" to get the user's attention for sure
|
||||
* Removed all element-specific SetFocus methods and made it available to ALL elements
|
||||
* Listbox - no_scrollbar parameter added. If True then no scrollbar will be shown
|
||||
* NEW finalize bool parameter added to Window. Removes need to "chain" .Finalize() call.
|
||||
* NEW element_justification parameter for Column, Frame, Tab Elements and Window
|
||||
* Valid values are 'left', 'right', 'center'. Only first letter checked so can use 'l', 'c','r'
|
||||
* Default = 'left'
|
||||
* Result is that all Elements INSIDE of this container will be justified as specified
|
||||
* Works well with new Sizer Elements
|
||||
* NEW justification parameter for Column elements.
|
||||
* Justifies Column AND the row it's on to this setting (left, right, center)
|
||||
* Enables individual rows to be justified in addition to the entire window
|
||||
* NEW Sizer Element
|
||||
* Has width and height parameters. Can set one or both
|
||||
* Causes the element it is contained within to expand according to width and height of Sizer Element
|
||||
* Helps greatly with centering. Frames will shrink to fit the contents for example. Use Sizer to pad out to right size
|
||||
* Added Window.visibility_changed to match the PySimpleGUIQt call
|
||||
* Fixed Debugger so that popout window shows any newly added locals
|
||||
|
||||
|
||||
## 4.4 PySimpleGUI Release 5-Sep-2019
|
||||
|
||||
* window() - "Calling" your Window object will perform a Read call
|
||||
* InputText - move cursor to end following Update
|
||||
* Shortcuts - trying to get a manageable and stable set of Normal, Short, Super-short
|
||||
* DD - DropDown (Combo)
|
||||
* LB, LBox - Listbox
|
||||
* R, Rad - Radio
|
||||
* ML, MLine - Multiline
|
||||
* BMenu - ButtonMenu
|
||||
* PBar, Prog - ProgressBar
|
||||
* Col - Column
|
||||
* Listbox - new method GetIndexes returns currently selected items as a list of indexes
|
||||
* Output - new method Get returns the contents of the output element
|
||||
* Button - For Macs don't don't allow setting button color. Previously only warned
|
||||
* ButtonMenu - new Click method will click the button just like a normal Button's Click method
|
||||
* Column scrolling finally works correctly with mousewheel. Shift+Mouse Scroll will scroll horizontally
|
||||
* Table - Get method is a dummy version a Get because Qt port got a real Get method
|
||||
* Table - Will add numerical column headers if Column Headsing is set to None when creating Table Element
|
||||
* Table - FIXED the columns crazily resizing themselves bug!!
|
||||
* Table - Can resize individual columns now
|
||||
* Tree - was not returning Keys but instead the string representation of the key
|
||||
* SetIcon will set to default base64 icon if there's an error loading icon
|
||||
* Fix for duplicate key error. Was attempting to add a "unique key counter" onto end of keys if duplicate, but needed to turn into string first
|
||||
* Columns
|
||||
* No longer expand nor fill
|
||||
* Sizing works for both scrolled and normal
|
||||
* Setting focus - fixed bug when have tabs, columns, frames that have elements that can get the focus. Setting focus on top-level window
|
||||
* InputText elements will now cause rows to expand due to X direction expansion
|
||||
* Frame - Trying to set the size but doesn't seem to be setting it correctly
|
||||
* Tabs will now expand & fill now (I hope this is OK!!!)
|
||||
|
||||
|
||||
|
||||
### Upcoming
|
||||
Make suggestions people! Future release features
|
||||
|
@ -785,7 +853,7 @@ The PySimpleGUI Organization
|
|||
|
||||
This documentation as well as all PySimpleGUI code is Copyright 2018, 2019 by PySimpleGUI.org
|
||||
|
||||
PySimpleGUI@PySimpleGUI.org
|
||||
Send correspondance to PySimpleGUI@PySimpleGUI.com
|
||||
|
||||
## License
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
|||
import inspect
|
||||
import PySimpleGUIlib as sg
|
||||
import PySimpleGUI as sg
|
||||
|
||||
psg_members = inspect.getmembers(sg)
|
||||
psg_members = inspect.getmembers(PySimpleGUI)
|
||||
|
||||
psg_funcs = [o for o in psg_members if inspect.isfunction(o[1])]
|
||||
psg_classes = [o for o in psg_members if inspect.isclass(o[1])]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from inspect import getmembers, isfunction, isclass, getsource, signature, _empty
|
||||
from inspect import getmembers, isfunction, isclass, getsource, signature, _empty, isdatadescriptor
|
||||
from datetime import datetime
|
||||
import PySimpleGUIlib
|
||||
import click
|
||||
|
@ -6,6 +6,17 @@ import logging
|
|||
import json
|
||||
import re
|
||||
import os
|
||||
|
||||
########################################################
|
||||
# _ _ _ #
|
||||
# | | | | | | #
|
||||
# | |_ ___ _ __ ___ _ __ | | __ _| |_ ___ ___ #
|
||||
# | __/ _ \ '_ ` _ \| '_ \| |/ _` | __/ _ \/ __| #
|
||||
# | || __/ | | | | | |_) | | (_| | || __/\__ \ #
|
||||
# \__\___|_| |_| |_| .__/|_|\__,_|\__\___||___/ #
|
||||
# | | #
|
||||
# |_| #
|
||||
########################################################
|
||||
TAB_char = ' '
|
||||
TABLE_TEMPLATE='''
|
||||
Parameter Descriptions:
|
||||
|
@ -18,7 +29,18 @@ TABLE_TEMPLATE='''
|
|||
'''
|
||||
TABLE_ROW_TEMPLATE = '|{name}|{desc}|'
|
||||
TABLE_RETURN_TEMPLATE = '|||\n| **return** | {return_guy} |'
|
||||
TABLE_Only_table_RETURN_TEMPLATE = '''|Name|Meaning|\n|---|---|\n| **return** | $ |'''
|
||||
TABLE_Only_table_RETURN_TEMPLATE = '''|Name|Meaning|\n|---|---|\n| **return** | $ |''' # $ - is the part for return value
|
||||
|
||||
##############################################################################
|
||||
# _ _ #
|
||||
# | | | | #
|
||||
# ___ _ _ ___| |_ ___ _ __ ___ ___| | __ _ ___ ___ ___ ___ #
|
||||
# / __| | | / __| __/ _ \| '_ ` _ \ / __| |/ _` / __/ __|/ _ \/ __| #
|
||||
# | (__| |_| \__ \ || (_) | | | | | | | (__| | (_| \__ \__ \ __/\__ \ #
|
||||
# \___|\__,_|___/\__\___/|_| |_| |_| \___|_|\__,_|___/___/\___||___/ #
|
||||
# #
|
||||
# #
|
||||
##############################################################################
|
||||
|
||||
from collections import namedtuple
|
||||
special_case = namedtuple('special_case', 'ok sig table just_text'.split(' '))
|
||||
|
@ -67,7 +89,7 @@ CLASS
|
|||
}
|
||||
"""
|
||||
|
||||
def get_params_part(code: str) -> dict:
|
||||
def get_params_part(code: str, versbose=True) -> dict:
|
||||
"""
|
||||
Find ":param " part in given "doc string".
|
||||
|
||||
|
@ -80,16 +102,15 @@ def get_params_part(code: str) -> dict:
|
|||
code = code.strip()
|
||||
|
||||
# if doc_string is empty
|
||||
if code == None: return {}
|
||||
elif '' == code.strip(): return {}
|
||||
elif ':param' not in code: return {}
|
||||
if code == None or code == '' or ':param' not in code:
|
||||
return {}
|
||||
elif ':return' in code: # strip ':return:'
|
||||
new_code = code[:code.index(':return:')]
|
||||
|
||||
regg_ = re.compile(r':return[\d\D]*?:param', flags=re.MULTILINE)
|
||||
if len(list(regg_.finditer(new_code))) > 0:
|
||||
if versbose:
|
||||
print(f'warning-> ":return" MUST BY AT THE END. FIX IT NOW in {name_}!!!\nBut i will try to parse it...')
|
||||
print(f'warning-> ":return" MUST BY AT THE END. FIX IT NOW in "{code}"!!!\nBut i will try to parse it...')
|
||||
code = re.sub(regg_, r':param', code)
|
||||
else:
|
||||
code = new_code
|
||||
|
@ -97,20 +118,17 @@ def get_params_part(code: str) -> dict:
|
|||
try:
|
||||
only_params = code[code.index(':param'):] # get_only_params_string(code)
|
||||
except Exception as e:
|
||||
if versbose: print(f'SORRY, fail at parsing that stuff in {name_}')
|
||||
if versbose:
|
||||
print(f'SORRY, fail at parsing that stuff in "{code}"')
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# making dict
|
||||
param_lines = only_params.split(':param ')
|
||||
param_lines = [re.sub(r'[ ]{2,}', ' ', i.strip(' ').strip('\t').replace('\n', ' '), flags=re.MULTILINE)
|
||||
for i in param_lines if i.strip()] # filter empty lines
|
||||
|
||||
args_kwargs_pairs = {}
|
||||
for index, i in enumerate(param_lines):
|
||||
for i in param_lines:
|
||||
|
||||
cols = i.split(':')
|
||||
param_name, els = cols[0], '\n'.join(
|
||||
|
@ -130,137 +148,177 @@ def get_return_part(code: str, line_break=None) -> str:
|
|||
|
||||
if ':return:' not in code:
|
||||
return ''
|
||||
|
||||
return code[code.index(':return:')+len(':return:'):].strip().replace('\n', line_break)
|
||||
|
||||
|
||||
def special_cases(function_name, sig, doc_string, line_break=None):
|
||||
|
||||
def special_cases(function_name, function_obj, sig, doc_string, line_break=None):
|
||||
|
||||
doca, params_names = doc_string.strip(), list(dict(sig).keys())
|
||||
if 'self' in params_names and len(params_names) == 1 and not doca:
|
||||
"""
|
||||
def Get(self):
|
||||
''' '''
|
||||
only_self = 'self' in params_names and len(params_names) == 1
|
||||
|
||||
->
|
||||
```python
|
||||
Get()
|
||||
```
|
||||
"""
|
||||
return special_case(ok=True, just_text=f'\n\n```python\n{function_name}()\n```\n\n', sig='', table='')
|
||||
############################################################################
|
||||
# _ _ #
|
||||
# | | | | #
|
||||
# ___| | __ _ ___ ___ _ __ _ __ ___ _ __ ___ _ __| |_ _ _ #
|
||||
# / __| |/ _` / __/ __| | '_ \| '__/ _ \| '_ \ / _ \ '__| __| | | | #
|
||||
# | (__| | (_| \__ \__ \ | |_) | | | (_) | |_) | __/ | | |_| |_| | #
|
||||
# \___|_|\__,_|___/___/ | .__/|_| \___/| .__/ \___|_| \__|\__, | #
|
||||
# | | | | __/ | #
|
||||
# |_| |_| |___/ #
|
||||
############################################################################
|
||||
|
||||
"""
|
||||
# TEMPLATE1
|
||||
|
||||
# -return -param
|
||||
elif 'self' in params_names and len(params_names) == 1 and doca and ':param' not in doca and ':return:' not in doca:
|
||||
"""
|
||||
def Get(self):
|
||||
'''
|
||||
''' '''
|
||||
# TEMPLATE2 -return -param
|
||||
def Get(self):
|
||||
'''
|
||||
blah blah blah
|
||||
'''
|
||||
|
||||
->
|
||||
|
||||
```python
|
||||
Get() # blah blah blah
|
||||
```
|
||||
|
||||
"""
|
||||
return special_case(ok=True, just_text=f'\n\n{doca}\n\n```python\n{function_name}()\n```\n\n', sig='', table='')
|
||||
|
||||
# +return -param
|
||||
elif 'self' in params_names and len(params_names) == 1 and doca and ':param' not in doca and ':return:' in doca:
|
||||
"""
|
||||
# TEMPLATE3 +return -param
|
||||
def Get(self):
|
||||
'''
|
||||
blah blah blah
|
||||
:return: blah-blah
|
||||
'''
|
||||
"""
|
||||
|
||||
if is_propery(function_obj):
|
||||
# TEMPLATE1
|
||||
if only_self and not doca:
|
||||
return special_case(ok=True, just_text=f'\n\n#### property: {function_name}\n\n', sig='', table='')
|
||||
# TEMPLATE2
|
||||
elif only_self and doca and ':param' not in doca and ':return:' not in doca:
|
||||
return special_case(ok=True, just_text=f'\n\n#### property: {function_name}\n{get_doc_desc(doca, function_obj)}\n\n', sig='', table='')
|
||||
# TEMPLATE3
|
||||
elif only_self and doca and ':param' not in doca and ':return:' in doca:
|
||||
return_part, desc = get_return_part(doca, line_break=line_break), get_doc_desc(doca, function_obj)
|
||||
return special_case(ok=True, just_text='',
|
||||
sig=f'\n\n#### property: {function_name}\n{desc}\n\n',
|
||||
table=TABLE_Only_table_RETURN_TEMPLATE.replace('$', return_part) + '\n\n')
|
||||
|
||||
->
|
||||
################################################################################################################
|
||||
# _ _ _ _ _ #
|
||||
# | | | | | | | | | | #
|
||||
# _ __ ___ _ __ _ __ ___ __ _| | ___| | __ _ ___ ___ _ __ ___ ___| |_| |__ ___ __| |___ #
|
||||
# | '_ \ / _ \| '__| '_ ` _ \ / _` | | / __| |/ _` / __/ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __| #
|
||||
# | | | | (_) | | | | | | | | (_| | | | (__| | (_| \__ \__ \ | | | | | | __/ |_| | | | (_) | (_| \__ \ #
|
||||
# |_| |_|\___/|_| |_| |_| |_|\__,_|_| \___|_|\__,_|___/___/ |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/ #
|
||||
# #
|
||||
# #
|
||||
################################################################################################################
|
||||
|
||||
```python
|
||||
Get()
|
||||
```
|
||||
"""
|
||||
# TEMPLATE1
|
||||
|
||||
*table*
|
||||
|
||||
"""
|
||||
return_part, desc = get_return_part(doca, line_break=line_break), get_doc_desc(doca)
|
||||
return special_case(ok=True, just_text='',
|
||||
sig=f'\n\n{desc}\n\n`{function_name}()`\n\n',
|
||||
table=TABLE_Only_table_RETURN_TEMPLATE.replace('$', return_part) + '\n\n')
|
||||
|
||||
# +return -param
|
||||
elif 'self' in params_names and len(params_names) == 1 and doca and ':param' not in doca and ':return:' in doca:
|
||||
"""
|
||||
def Get(self):
|
||||
''' '''
|
||||
# TEMPLATE2 -return -param
|
||||
def Get(self):
|
||||
'''
|
||||
blah blah blah
|
||||
'''
|
||||
# TEMPLATE3 +return -param
|
||||
def Get(self):
|
||||
'''
|
||||
blah blah blah
|
||||
:return: blah-blah
|
||||
'''
|
||||
# TEMPLATE4 -return +param
|
||||
def SetFocus(self, elem):
|
||||
'''
|
||||
blah blah blah
|
||||
|
||||
:param elem: qwerty
|
||||
'''
|
||||
"""
|
||||
"""
|
||||
|
||||
# TEMPLATE1
|
||||
if only_self and not doca:
|
||||
return special_case(ok=True, just_text=f'\n\n```python\n{function_name}()\n```\n\n', sig='', table='')
|
||||
# TEMPLATE2
|
||||
elif only_self and doca and ':param' not in doca and ':return:' not in doca:
|
||||
return special_case(ok=True, just_text=f'\n\n{doca}\n\n```python\n{function_name}()\n```\n\n', sig='', table='')
|
||||
# TEMPLATE3
|
||||
elif only_self and doca and ':param' not in doca and ':return:' in doca:
|
||||
return_part, desc = get_return_part(doca, line_break=line_break), get_doc_desc(doca, function_obj)
|
||||
return special_case(ok=True, just_text='', sig=f'\n\n{desc}\n\n`{function_name}()`\n\n',
|
||||
table=TABLE_Only_table_RETURN_TEMPLATE.replace('$', return_part) + '\n\n')
|
||||
# TEMPLATE4
|
||||
elif only_self and doca and ':param' not in doca and ':return:' in doca:
|
||||
return special_case(ok=False, just_text='', sig='', table='')
|
||||
|
||||
|
||||
return special_case(ok=False, just_text='', sig='', table='')
|
||||
|
||||
|
||||
def get_doc_desc(doc_string):
|
||||
def get_doc_desc(doc, original_obj):
|
||||
|
||||
if ':param' in doc_string: doc_string = doc_string[:doc_string.index(':param')]
|
||||
if ':return:' in doc_string: doc_string = doc_string[:doc_string.index(':return:')]
|
||||
if ':param' in doc_string: doc_string = doc_string[:doc_string.index(':param')]
|
||||
if ':return:' in doc_string: doc_string = doc_string[:doc_string.index(':return:')]
|
||||
return_in = ':return' in doc
|
||||
param_in = ':param' in doc
|
||||
|
||||
if return_in and param_in and doc.index(':return') < doc.index(':param'):
|
||||
logging.error(f'BS. You need to FIX IT. PROBLEM ":return:" BEFORE ":param:" in "{original_obj.__name__}"')
|
||||
|
||||
desc = doc_string.strip().replace(' ', '')
|
||||
if ':param' in doc: doc = doc[:doc.index(':param')]
|
||||
if ':return' in doc: doc = doc[:doc.index(':return:')]
|
||||
if ':param' in doc: doc = doc[:doc.index(':param')]
|
||||
if ':return' in doc: doc = doc[:doc.index(':return:')]
|
||||
|
||||
desc = doc.strip().replace(' ', '')
|
||||
|
||||
return f'\n{desc}' if desc else ''
|
||||
|
||||
def is_propery(func):
|
||||
return isdatadescriptor(func) and not isfunction(func)
|
||||
|
||||
def get_sig_table_parts(function_obj, function_name, doc_string, logger=None, is_method=False, line_break=None, insert_md_section_for__class_methods=False):
|
||||
"""
|
||||
Convert "function + __doc__" tp "method call + params table" in MARKDOWN
|
||||
"""
|
||||
|
||||
""" Convert "function + __doc__" tp "method call + params table" in MARKDOWN """
|
||||
doc_string = doc_string.strip()
|
||||
|
||||
# qpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqp
|
||||
# 0 0 Making INIT_CALL 0 0 #
|
||||
# qpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqp
|
||||
|
||||
try:
|
||||
sig, rows = signature(function_obj).parameters, []
|
||||
rows = []
|
||||
sig = {'self': None} if is_propery(function_obj) else signature(function_obj).parameters
|
||||
except Exception as e:
|
||||
if logger: logger.error(f'PROBLEM WITH "{function_obj}" "{function_name}":\nit\'s signature is BS. Ok, I will just return \'\' for \'signature\' and \'param_table\'\nOR BETTER - delete it from the 2_readme.md.\n======')
|
||||
return '', ''
|
||||
for index, key in enumerate(sig):
|
||||
val = sig[key].default
|
||||
if 'self' == str(key):
|
||||
continue
|
||||
if val == _empty: rows.append(key)
|
||||
elif val == None: rows.append(f'{key}=None')
|
||||
elif type(val) is int: rows.append(f'{key}={val}')
|
||||
elif type(val) is str: rows.append(f'{key}="{val}"')
|
||||
elif type(val) is tuple: rows.append(f'{key}={val}')
|
||||
elif type(val) is bool: rows.append(f'{key}={val}')
|
||||
else:
|
||||
raise Exception(f'IDK this type -> {key, val}')
|
||||
if not is_propery(function_obj):
|
||||
for key in sig:
|
||||
val = sig[key].default
|
||||
if 'self' == str(key):
|
||||
continue
|
||||
if val == _empty: rows.append(key)
|
||||
elif val == None: rows.append(f'{key}=None')
|
||||
elif type(val) is int: rows.append(f'{key}={val}')
|
||||
elif type(val) is str: rows.append(f'{key}="{val}"')
|
||||
elif type(val) is tuple: rows.append(f'{key}={val}')
|
||||
elif type(val) is bool: rows.append(f'{key}={val}')
|
||||
else:
|
||||
raise Exception(f'IDK this type -> {key, val}')
|
||||
|
||||
|
||||
sig_content = f',\n{TAB_char}'.join(rows) if len(rows) > 2 else f', '.join(rows)
|
||||
sig_content = f',\n{TAB_char}'.join(rows) if len(rows) > 2 else f', '.join(rows) if rows else ''
|
||||
# # # make 2 line signature into 1-line
|
||||
# # # sig_content = f',\n{TAB_char}'.join(rows)
|
||||
# # # if sig_content.count('\n') < 3: sig_content = re.sub(r'\n[ \t]{,8}', ' ', sig_content, flags=re.MULTILINE)
|
||||
|
||||
sign = "\n\n{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string), function_name, sig_content)
|
||||
sign = "\n\n{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string, function_obj), function_name, sig_content)
|
||||
|
||||
if is_method:
|
||||
if insert_md_section_for__class_methods:
|
||||
sign = "#### {1}\n\n{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string), function_name, sig_content)
|
||||
# sign = "#### {1}\n\n{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string, function_obj), function_name, sig_content)
|
||||
sign = "\n\n{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string, function_obj), function_name, sig_content)
|
||||
else:
|
||||
sign = "{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string), function_name, sig_content)
|
||||
sign = "{0}\n\n```\n{1}({2})\n```".format(get_doc_desc(doc_string, function_obj), function_name, sig_content)
|
||||
# --------------
|
||||
# SPECIAL CASES
|
||||
# --------------
|
||||
result = special_cases(function_name, sig, doc_string, line_break=line_break)
|
||||
result = special_cases(function_name, function_obj, sig, doc_string, line_break=line_break)
|
||||
if result.ok:
|
||||
if result.just_text:
|
||||
return result.just_text, ''
|
||||
|
@ -308,6 +366,13 @@ def pad_n(text): return f'\n{text}\n'
|
|||
|
||||
|
||||
def render(injection, logger=None, line_break=None, insert_md_section_for__class_methods=False):
|
||||
|
||||
try:
|
||||
if 'skip readme' in injection['function_object'].__doc__:
|
||||
return ''
|
||||
except Exception as e:
|
||||
return ''
|
||||
|
||||
if injection['part1'] == 'func': # function
|
||||
sig, table = get_sig_table_parts(function_obj=injection['function_object'],
|
||||
function_name=injection['part2'],
|
||||
|
@ -336,8 +401,7 @@ def readfile(fname):
|
|||
return ff.read()
|
||||
|
||||
|
||||
def main(do_full_readme=False, files_to_include: list = [], logger=None, output_name=None, delete_html_comments=True, delete_x3_newlines=True, allow_multiple_tags=True, line_break=None, insert_md_section_for__class_methods=True, remove_repeated_sections_classmethods=False):
|
||||
|
||||
def main(do_full_readme=False, files_to_include: list = [], logger:object=None, output_name:str=None, delete_html_comments:bool=True, delete_x3_newlines:bool=True, allow_multiple_tags:bool=True, line_break:str=None, insert_md_section_for__class_methods:bool=True, remove_repeated_sections_classmethods:bool=False, output_repeated_tags:bool=False, skip_dunder_method:bool=True):
|
||||
"""
|
||||
Goal is:
|
||||
1) load 1_.md 2_.md 3_.md 4_.md
|
||||
|
@ -347,63 +411,84 @@ def main(do_full_readme=False, files_to_include: list = [], logger=None, output_
|
|||
5) replaces classes, functions.
|
||||
6) join 1 big readme file
|
||||
|
||||
:param do_full_readme: if False - use only 2_readme.md
|
||||
:param files_to_include: list of markdown files to include in output markdown
|
||||
:param logger: logger object from logging module
|
||||
:param delete_html_comments: flag for preprocessing input markwon text e.g. deleting every html tag, that is injection_point
|
||||
:param allow_multiple_tags: flag for replacing every tag in "input markdown text"
|
||||
:param delete_x3_newlines: flag for deleting '\\n\\n\\n' in final output makrdown text
|
||||
:param output_name: base filename of output markdown file
|
||||
:param line_break: linebreak_character in "return part"
|
||||
:param do_full_readme: (bool=True) if False - use only 2_readme.md
|
||||
:param files_to_include: (list=[]) list of markdown files to include in output markdown
|
||||
:param logger: (object=None) logger object from logging module
|
||||
:param output_name: (str=None) base filename of output markdown file
|
||||
:param delete_html_comments: (bool=True) flag for preprocessing input markwon text e.g. deleting every html tag, that is injection_point
|
||||
:param delete_x3_newlines: (bool=True) flag for deleting '\\n\\n\\n' in final output makrdown text
|
||||
:param allow_multiple_tags: (bool=True) flag for replacing every tag in "input markdown text"
|
||||
:param line_break: (str=None) linebreak_character in "return part"
|
||||
:param insert_md_section_for__class_methods: (bool=True) insert '###' sign to class_methods when outputing in markdown
|
||||
:param remove_repeated_sections_classmethods: (bool=True)
|
||||
:param output_repeated_tags: (bool=True) log REPEATED tags in file
|
||||
:param skip_dunder_method: (bool=True) skip __something__ methods in classes
|
||||
"""
|
||||
|
||||
if logger: logger.info(f'STARTING')
|
||||
|
||||
# 888888888888888888888888888888888888888888
|
||||
# =========== 1 loading files =========== #
|
||||
# 888888888888888888888888888888888888888888
|
||||
HEADER_top_part = readfile('1_HEADER_top_part.md') # 1
|
||||
readme = readfile('2_readme.md') # 2
|
||||
FOOTER = readfile('3_FOOTER.md') # 3
|
||||
Release_notes = readfile('4_Release_notes.md') # 4
|
||||
|
||||
|
||||
readme = readfile('2_readme.md')
|
||||
# 8888888888888888888888888888888888888888888888888888888888888888888888888
|
||||
# =========== 2 GET classes, funcions, varialbe a.k.a. memes =========== #
|
||||
# 8888888888888888888888888888888888888888888888888888888888888888888888888
|
||||
psg_members = getmembers(PySimpleGUIlib)
|
||||
psg_members = getmembers(PySimpleGUIlib) # variables, functions, classes
|
||||
|
||||
psg_funcs = [o for o in psg_members if isfunction(o[1])]
|
||||
psg_classes = [o for o in psg_members if isclass(o[1])]
|
||||
psg_classes_ = list(set([i[1] for i in psg_classes])) # filtering
|
||||
psg_funcs = [o for o in psg_members if isfunction(o[1])] # only functions
|
||||
psg_classes = [o for o in psg_members if isclass(o[1])] # only classes
|
||||
psg_classes_ = list(set([i[1] for i in psg_classes])) # boildown B,Btn,Butt -into-> Button
|
||||
psg_classes = list(zip([i.__name__ for i in psg_classes_], psg_classes_))
|
||||
|
||||
# IlilIlilIlilIlilIlilIlilIlilIlilIlilIlIlIl
|
||||
# ilIli- | | -ilIli
|
||||
# ilIli- _ __ ___ ___ __ _| |_ -ilIli
|
||||
# ilIli- | '_ ` _ \ / _ \/ _` | __| -ilIli
|
||||
# ilIli- | | | | | | __/ (_| | |_ -ilIli
|
||||
# ilIli- |_| |_| |_|\___|\__,_|\__| -ilIli
|
||||
|
||||
# 8888888888888888888888888888888888888888888888888888888
|
||||
# =========== 3 find all tags in 2_readme =========== #
|
||||
# 8888888888888888888888888888888888888888888888888888888
|
||||
# PLAN:
|
||||
# (1) REMOVE HEADER
|
||||
|
||||
# strip top of the file head
|
||||
# (2) find good tags e.g. <!-- <+func.PopupScrolled+> -->
|
||||
|
||||
# (3) (optional) find '_' tags e.g.
|
||||
# '_' tag - is a tag, that has '_' after '.'
|
||||
#
|
||||
# Example: <!-- <+func._PopupScrolled+> -->
|
||||
# /\
|
||||
# |---that's sign of a bad tags
|
||||
|
||||
# (4) (optional) log repeated tags.
|
||||
# like <!-- <+class.B+> -->
|
||||
# and
|
||||
# <!-- <+class.Button+> -->
|
||||
# 8888888888888888888888888888888888888888888888888888888
|
||||
|
||||
# >1 REMOVE HEADER
|
||||
started_mark = '<!-- Start from here -->'
|
||||
if started_mark in readme:
|
||||
readme = readme[readme.index(started_mark)+len(started_mark):]
|
||||
|
||||
# find with regex
|
||||
regex_pattern = re.compile(r'<!-- <\+[a-zA-Z_]+[\d\w_]*\.([a-zA-Z_]+[\d\w_]*)\+> -->')
|
||||
mark_points = [i for i in readme.split('\n') if regex_pattern.match(i)]
|
||||
# 2> find good tags
|
||||
re_tags = re.compile(r'<!-- <\+[a-zA-Z_]+[\d\w_]*\.([a-zA-Z_]+[\d\w_]*)\+> -->')
|
||||
mark_points = [i for i in readme.split('\n') if re_tags.match(i)]
|
||||
|
||||
special_dunder_methods = ['init', 'repr', 'str', 'next']
|
||||
# 3> find '_' tags OPTION
|
||||
if skip_dunder_method:
|
||||
re_bad_tags = re.compile(r'<!-- <\+[a-zA-Z_]+[\d\w_]*\.([_]+[\d\w_]*)\+> -->')
|
||||
for i in readme.split('\n'):
|
||||
if re_bad_tags.match(i.strip()):
|
||||
if not [s_tag for s_tag in special_dunder_methods if s_tag in i.strip()]:
|
||||
readme = readme.replace(i, '\n')
|
||||
|
||||
# if there are REPEATED tags -> show them.
|
||||
# if not allow_multiple_tags and len(list(set(mark_points))) != len(mark_points):
|
||||
# [mark_points.remove(x) for x in set(mark_points)]
|
||||
# if logger:
|
||||
# logger.error("You have repeated tags! \n {0}".format(
|
||||
# ','.join(mark_points)))
|
||||
# return ''
|
||||
# 4> log repeated tags
|
||||
if output_repeated_tags:
|
||||
if not allow_multiple_tags and len(list(set(mark_points))) != len(mark_points):
|
||||
mark_points_copy = mark_points[:]
|
||||
[mark_points_copy.remove(x) for x in set(mark_points)]
|
||||
if logger:
|
||||
logger.error("You have repeated tags! \n {0}".format(
|
||||
','.join(mark_points_copy)))
|
||||
return ''
|
||||
|
||||
# 8888888888888888888888888888888888888888888888888888888888888
|
||||
# =========== 4 structure tags and REAL objects =========== #
|
||||
|
@ -417,9 +502,7 @@ def main(do_full_readme=False, files_to_include: list = [], logger=None, output_
|
|||
for tag in func_tags:
|
||||
|
||||
try:
|
||||
__, function_name = tag.split('.')
|
||||
function_name = function_name.split('+')[0]
|
||||
part2 = function_name
|
||||
function_name = part2 = tag.split('.')[1].split('+')[0]
|
||||
|
||||
# {{{{{{{{{ filter number }}}}}}}}}
|
||||
number = ''
|
||||
|
@ -430,10 +513,12 @@ def main(do_full_readme=False, files_to_include: list = [], logger=None, output_
|
|||
founded_function = [func for func_name,
|
||||
func in psg_funcs if func_name == function_name]
|
||||
if not founded_function:
|
||||
if logger: logger.error(f'function "{function_name}" not found in PySimpleGUI')
|
||||
if logger:
|
||||
logger.error(f'function "{function_name}" not found in PySimpleGUI')
|
||||
continue
|
||||
if len(founded_function) > 1:
|
||||
if logger: logger.error(f'more than 1 function named "{function_name}" found in PySimpleGUI')
|
||||
if logger:
|
||||
logger.error(f'more than 1 function named "{function_name}" found in PySimpleGUI')
|
||||
continue
|
||||
|
||||
# {{{{{{{{{ collect }}}}}}}}}
|
||||
|
@ -447,15 +532,14 @@ def main(do_full_readme=False, files_to_include: list = [], logger=None, output_
|
|||
})
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.error(f' {str(e)}')
|
||||
logger.error(f' General error in parsing function tag: tag = "{tag}"; error="{str(e)}"')
|
||||
continue
|
||||
|
||||
# 0===0 classes 0===0
|
||||
for tag in classes_method_tags:
|
||||
try:
|
||||
class_name, method_name = tag.split('.')
|
||||
class_name, method_name = class_name.split('+')[-1], method_name.split('+')[0]
|
||||
part1, part2 = class_name, method_name
|
||||
class_name, method_name = part1, part2 = class_name.split('+')[-1], method_name.split('+')[0]
|
||||
|
||||
# {{{{{{{{{ filter number }}}}}}}}}
|
||||
number = ''
|
||||
|
@ -463,34 +547,29 @@ def main(do_full_readme=False, files_to_include: list = [], logger=None, output_
|
|||
number, method_name = part2[0], part2[1:]
|
||||
|
||||
# {{{{{{{{{ find class }}}}}}}}}
|
||||
founded_class = [a_class_obj for a_class_name,
|
||||
a_class_obj in psg_classes if a_class_name == class_name]
|
||||
founded_class = [a_class_obj
|
||||
for a_class_name, a_class_obj in psg_classes
|
||||
if a_class_name == class_name]
|
||||
if not founded_class:
|
||||
if logger: logger.error(f'class "{tag}" not found in PySimpleGUI')
|
||||
if logger: logger.error(f'skipping tag "{tag}", WHY: not found in PySimpleGUI')
|
||||
continue
|
||||
if len(founded_class) > 1:
|
||||
if logger: logger.error(f'more than 1 class named "{tag}" found in PySimpleGUI')
|
||||
if logger: logger.error(f'skipping tag "{tag}", WHY: found more than 1 class in PySimpleGUI')
|
||||
continue
|
||||
|
||||
# {{{{{{{{{ find method }}}}}}}}}
|
||||
try:
|
||||
if method_name != 'doc':
|
||||
founded_method = getattr(founded_class[0], method_name)
|
||||
# GLG.append([founded_method, founded_class[0], method_name])
|
||||
# string_type = str(type(founded_method))
|
||||
# if 'property' in string_type or 'bound' in string_type:
|
||||
# print(string_type)
|
||||
# # import pdb; pdb.set_trace();
|
||||
# if logger:
|
||||
# logger.error(f'Property "{founded_method}" is not parsed.')
|
||||
# continue
|
||||
else:
|
||||
founded_method = None
|
||||
except AttributeError as e:
|
||||
if logger: logger.error(f'METHOD not found!: {str(e)}')
|
||||
if logger:
|
||||
logger.error(f'METHOD not found!: {str(e)}')
|
||||
continue
|
||||
except Exception as e:
|
||||
if logger: logger.error(str(e))
|
||||
if logger:
|
||||
logger.error(f'Error in finding the METHOD: {str(e)}')
|
||||
continue
|
||||
|
||||
# {{{{{{{{{ collect }}}}}}}}}
|
||||
|
@ -504,66 +583,86 @@ def main(do_full_readme=False, files_to_include: list = [], logger=None, output_
|
|||
})
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.error(f'```````````````````````{str(e)}')
|
||||
logger.error(f' General error in parsing class_method tag: tag = "{tag}"; error="{str(e)}"')
|
||||
continue
|
||||
|
||||
# 888888888888888888888888888888888888888
|
||||
# =========== 5 injecting =========== #
|
||||
# 888888888888888888888888888888888888888
|
||||
# PLAN:
|
||||
# (1) replace tags in 2_readme
|
||||
# with properly formateed text
|
||||
# (2) log some data
|
||||
# 8888888888888888888888888888888888888888888888888888888
|
||||
|
||||
|
||||
# 1> log some data
|
||||
success_tags = []
|
||||
bad_tags = []
|
||||
for injection in injection_points:
|
||||
if injection['part2'] == 'doc': # our special snowflake "doc"
|
||||
|
||||
# SPECIAL CASE: X.doc tag
|
||||
if injection['part2'] == 'doc':
|
||||
readme = readme.replace(injection['tag'], injection['parent_class'].__doc__)
|
||||
|
||||
else:
|
||||
tag = injection['tag']
|
||||
content = render(injection, logger=logger, line_break=line_break, insert_md_section_for__class_methods=insert_md_section_for__class_methods,)
|
||||
content = render(injection, logger=logger, line_break=line_break,
|
||||
insert_md_section_for__class_methods=insert_md_section_for__class_methods,)
|
||||
|
||||
tag = injection["tag"]
|
||||
if content:
|
||||
success_tags.append(f'{tag} - COMPLETE')
|
||||
else:
|
||||
bad_tags.append(f'{tag} - FAIL')
|
||||
readme = readme.replace(injection['tag'], content)
|
||||
|
||||
readme = readme.replace(tag, content)
|
||||
|
||||
# 2> log some data
|
||||
if logger:
|
||||
success_tags_str = '\n'.join(success_tags).strip()
|
||||
bad_tags_str = '\n'.join(bad_tags).strip()
|
||||
|
||||
# good message
|
||||
good_message = f'DONE {len(success_tags)} TAGS:\n' + '\n'.join(success_tags) if success_tags_str else 'All tags are wrong//'
|
||||
# bad message
|
||||
bad_message = f'FAIL WITH {len(bad_tags)} TAGS:\n' + '\n'.join(bad_tags) if bad_tags_str else 'No bad tags, YES!'
|
||||
|
||||
logger.info(good_message)
|
||||
logger.info(bad_message)
|
||||
|
||||
|
||||
# 8888888888888888888888888888888888
|
||||
# =========== 6 join =========== #
|
||||
# 8888888888888888888888888888888888
|
||||
|
||||
files = []
|
||||
if 0 in files_to_include: files.append(HEADER_top_part)
|
||||
if 0 in files_to_include: files.append(readfile('1_HEADER_top_part.md'))
|
||||
if 1 in files_to_include: files.append(readme)
|
||||
if 2 in files_to_include: files.append(FOOTER)
|
||||
if 3 in files_to_include: files.append(Release_notes)
|
||||
if 2 in files_to_include: files.append(readfile('3_FOOTER.md'))
|
||||
if 3 in files_to_include: files.append(readfile('4_Release_notes.md'))
|
||||
|
||||
Joined_MARKDOWN = '\n\n'.join(files) if do_full_readme or files else readme
|
||||
|
||||
if output_name:
|
||||
with open(output_name, 'w', encoding='utf-8') as ff:
|
||||
curr_dt = datetime.today().strftime('<!-- CREATED: %Y-%m-%d %H.%M.%S -->\n')
|
||||
content = curr_dt + Joined_MARKDOWN
|
||||
CURR_DT = datetime.today().strftime('<!-- CREATED: %Y-%m-%d %H.%M.%S -->\n')
|
||||
content = CURR_DT + Joined_MARKDOWN
|
||||
|
||||
# {{{{{{{{{ html removing }}}}}}}}}
|
||||
if delete_html_comments:
|
||||
if logger: logger.info('Deleting html comments')
|
||||
if logger:
|
||||
logger.info('Deleting html comments')
|
||||
|
||||
# remove html comments
|
||||
filt_readme = re.sub(
|
||||
r'<!--([\s\S]*?)-->', '\n', content, flags=re.MULTILINE)
|
||||
filt_readme = re.sub(r'<!--([\s\S]*?)-->', '\n', content, flags=re.MULTILINE)
|
||||
|
||||
for i in range(5):
|
||||
filt_readme = filt_readme.replace('\n\n\n', '\n\n')
|
||||
|
||||
# add staked_edit
|
||||
if '<!--stackedit_data:' in content:
|
||||
stackedit_data = content[content.index(
|
||||
'<!--stackedit_data:'):]
|
||||
filt_readme += stackedit_data
|
||||
stackedit_text = content[content.index('<!--stackedit_data:'):]
|
||||
filt_readme += stackedit_text
|
||||
|
||||
content = filt_readme
|
||||
|
||||
|
@ -573,12 +672,10 @@ def main(do_full_readme=False, files_to_include: list = [], logger=None, output_
|
|||
# removing spaces
|
||||
content = re.sub(r'^[ ]+$', '', content, flags=re.MULTILINE)
|
||||
# removing \n
|
||||
content = re.sub(r'\n{3,}', '\n\n',
|
||||
content, flags=re.MULTILINE)
|
||||
content = re.sub(r'\n{3,}', '\n\n', content, flags=re.MULTILINE)
|
||||
|
||||
# {{{{{{{{{ remove repeated sections classmethods }}}}}}}}}
|
||||
if remove_repeated_sections_classmethods:
|
||||
|
||||
rega = re.compile(r'((\#+\s\w+)\n\s){2}', flags=re.MULTILINE)
|
||||
for index, i in enumerate(re.finditer(rega, content)):
|
||||
print(f'{index} - > {i.group(0)}')
|
||||
|
@ -587,14 +684,16 @@ def main(do_full_readme=False, files_to_include: list = [], logger=None, output_
|
|||
# re
|
||||
# content = re.sub(rega, r'\1', content, flags=re.MULTILINE)
|
||||
|
||||
# FINISH
|
||||
content = content.strip()
|
||||
ff.write(content)
|
||||
# Write into a file
|
||||
ff.write(content.strip())
|
||||
|
||||
if logger:
|
||||
logger.info(f'ending. writing to a file///////////////')
|
||||
|
||||
if logger: logger.info(f'ending. writing to a file///////////////')
|
||||
return content
|
||||
|
||||
if logger: logger.error(f'Error in main')
|
||||
if logger:
|
||||
logger.error(f'Error in main')
|
||||
|
||||
|
||||
@click.command()
|
||||
|
@ -650,13 +749,27 @@ if __name__ == '__main__':
|
|||
cli()
|
||||
elif my_mode == 'debug-mode':
|
||||
main(files_to_include=[0, 1, 2, 3],
|
||||
output_name='johnson_n_johnson.txt',
|
||||
output_name='OUTPUT.txt',
|
||||
delete_html_comments=True)
|
||||
elif my_mode == 'debug-mode2':
|
||||
import logging; logger = logging.getLogger(__name__); logger.setLevel(logging.DEBUG)
|
||||
my_file = logging.FileHandler('usage.log.txt', mode='w'); my_file.setLevel(logging.DEBUG)
|
||||
log_file_name = 'usage.log.txt'
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
my_file = logging.FileHandler(log_file_name, mode='w')
|
||||
my_file.setLevel(logging.DEBUG)
|
||||
|
||||
formatter = logging.Formatter('%(asctime)s>%(levelname)s: %(message)s')
|
||||
my_file.setFormatter(formatter); logger.addHandler(my_file);
|
||||
my_file.setFormatter(formatter); logger.addHandler(my_file)
|
||||
|
||||
main(logger=logger, files_to_include=[1],
|
||||
output_name='johnson_n_johnson.txt',
|
||||
output_name='OUTPUT.txt',
|
||||
delete_html_comments=True)
|
||||
|
||||
'''
|
||||
notes:
|
||||
|
||||
Как оказалось, декоратор @property делает из метода вот что:
|
||||
- isdatadescriptor(class.method_as_property) вернет True
|
||||
'''
|
|
@ -0,0 +1,60 @@
|
|||
import inspect
|
||||
import PySimpleGUIlib
|
||||
|
||||
"""
|
||||
Create All Possible Tags
|
||||
Will output to STDOUT all of the different tags for classes, members and functions for a given PySimpleGUIlib.py
|
||||
file. Functions that begin with _ are filtered out from the list.
|
||||
Displays the results in a PySimpleGUI window which can be used to copy and paste into other places.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def new_name(name):
|
||||
name = name.replace("OK", "*1")
|
||||
name = name.replace("TK", "*2")
|
||||
name = name.replace("RGB", "*3")
|
||||
new = name[0].lower()
|
||||
for c in name[1:]:
|
||||
new += '_' + c.lower() if (c.isupper() or c == "*") else c
|
||||
new=new.replace("*1", "ok")
|
||||
new = new.replace("*2", "tk")
|
||||
new = new.replace("*3", "rgb")
|
||||
return new
|
||||
|
||||
layout = [[PySimpleGUIlib.Output(size=(600,300))]]
|
||||
window = PySimpleGUIlib.Window('Dump of tags', layout, resizable=True).Finalize()
|
||||
|
||||
psg_members = inspect.getmembers(PySimpleGUIlib)
|
||||
|
||||
psg_funcs = [o for o in psg_members if inspect.isfunction(o[1])]
|
||||
psg_classes = [o for o in psg_members if inspect.isclass(o[1])]
|
||||
# I don't know how this magic filtering works, I just know it works. "Private" stuff (begins with _) are somehow
|
||||
# excluded from the list with the following 2 lines of code. Very nicely done Kol-ee-ya!
|
||||
psg_classes_ = list(set([i[1] for i in psg_classes])) # filtering of anything that starts with _ (methods, classes, etc)
|
||||
psg_classes = list(zip([i.__name__ for i in psg_classes_], psg_classes_))
|
||||
|
||||
for pclass in sorted(psg_classes):
|
||||
if 'Tk' in pclass[0] or 'TK' in pclass[0] or 'Element' == pclass[0]: # or 'Window' == i[0]:
|
||||
continue
|
||||
# print(f'### {pclass[0]} Element')
|
||||
# print('')
|
||||
# print(f'<!-- <+{pclass[0]}.doc+> -->')
|
||||
# print(f'<!-- <+{pclass[0]}.__init__+> -->')
|
||||
print('')
|
||||
print(f'{pclass[0]} methods in PEP8 format --------------------------------------')
|
||||
for funcs in inspect.getmembers(pclass[1]):
|
||||
if '_' not in funcs[0]:
|
||||
# print(f'{pclass[0]}.{new_name(funcs[0])} = {pclass[0]}.{funcs[0]}') # version that has class on front
|
||||
print(f'{new_name(funcs[0])} = {funcs[0]}') # version without class on front (use for most)
|
||||
# print('\n'.join([f"#### {j[0]}\n\n<!-- <+{pclass[0]}.{j[0]}+> -->\n" for j in inspect.getmembers(pclass[1]) if '_' not in j[0]]))
|
||||
|
||||
# print('\n------------------------- Functions start here -------------------------\n')
|
||||
#
|
||||
for f in psg_funcs:
|
||||
if f[0][0] == '_':
|
||||
continue
|
||||
print(f'{new_name(f[0])} = {f[0]}')
|
||||
# print(f"<!-- <+func.{f[0]}+> -->")
|
||||
|
||||
window.Read()
|
File diff suppressed because it is too large
Load Diff
|
@ -34,8 +34,7 @@ for i in sorted(psg_classes):
|
|||
print('\n------------------------- Functions start here -------------------------\n')
|
||||
|
||||
for f in psg_funcs:
|
||||
if f[0][0] == '_':
|
||||
continue
|
||||
print(f"<!-- <+func.{f[0]}+> -->")
|
||||
if '_' != f[0][0]: # if doesn't START with _
|
||||
print(f"<!-- <+func.{f[0]}+> -->")
|
||||
|
||||
window.Read()
|
Loading…
Reference in New Issue